mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-31 03:36:51 +01:00 
			
		
		
		
	Compare commits
	
		
			71 Commits
		
	
	
		
			v0.8.4
			...
			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 | ||
|  | de7db08352 | ||
|  | ba5ded9bc2 | ||
|  | 4581dee4fc | ||
|  | d75d6e8b60 | ||
|  | 142ce642f0 | ||
|  | 7dead5ec96 | ||
|  | ddcf224db2 | ||
|  | daf794c4a2 | ||
|  | dac0124f0f | ||
|  | 15b7a30925 | ||
|  | 77ab71f26f | ||
|  | 68e1ba312d | ||
|  | 752fc6d619 | ||
|  | 16bcb27ca7 | ||
|  | 71d7d355a6 | ||
|  | 4188b392cc | ||
|  | 811fbfbb1e | ||
|  | 32eb7d3040 | ||
|  | 42763905d7 | ||
|  | f64300d1d6 | ||
|  | 1a723f0e8e | ||
|  | a86884c40e | ||
|  | 3f02e119f4 | ||
|  | 1bbd50aac7 | ||
|  | e7a0c32a68 | ||
|  | 7560bc9547 | ||
|  | 2b43340c90 | ||
|  | ff1620c923 | ||
|  | 1303053a27 | ||
|  | 91f194c821 | ||
|  | 478762f76c | ||
|  | 4d96d6a753 | ||
|  | 3cdcfbf4e5 | ||
|  | 4c302da503 | ||
|  | 26b5fe8f9d | 
| @@ -52,7 +52,7 @@ jobs: | |||||||
|           libqrencode |           libqrencode | ||||||
|           libsodium |           libsodium | ||||||
|           openal-soft |           openal-soft | ||||||
|         - export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.20.1/lib" |         - export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.21.0/lib" | ||||||
|         - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore |         - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore | ||||||
|         - test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc)) |         - test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc)) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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") | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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=gnu99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all |  | ||||||
| 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} | ||||||
| @@ -14,10 +13,26 @@ LDFLAGS ?= | |||||||
| LDFLAGS += ${USER_LDFLAGS} | LDFLAGS += ${USER_LDFLAGS} | ||||||
|  |  | ||||||
| OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o | OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o | ||||||
| OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o | OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o conference.o help.o input.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 += 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) | ||||||
| @@ -70,7 +85,7 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | |||||||
| 	fi | 	fi | ||||||
| 	@echo "  CC    $(@:$(BUILD_DIR)/%=%)" | 	@echo "  CC    $(@:$(BUILD_DIR)/%=%)" | ||||||
| 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c | 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c | ||||||
| 	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d | 	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c >$(BUILD_DIR)/$*.d | ||||||
|  |  | ||||||
| clean: | clean: | ||||||
| 	rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic | 	rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic | ||||||
|   | |||||||
| @@ -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.8.4' | version = '0.10.0' | ||||||
| # The full version, including alpha/beta/rc tags. | # The full version, including alpha/beta/rc tags. | ||||||
| release = '0.8.4' | 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. | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ endif | |||||||
| CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error") | CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error") | ||||||
| ifneq ($(CHECK_AUDIO_LIBS), error) | ifneq ($(CHECK_AUDIO_LIBS), error) | ||||||
|     LIBS += $(AUDIO_LIBS) |     LIBS += $(AUDIO_LIBS) | ||||||
|  |     LDFLAGS += -lm | ||||||
|     CFLAGS += $(AUDIO_CFLAGS) |     CFLAGS += $(AUDIO_CFLAGS) | ||||||
|     OBJ += $(AUDIO_OBJ) |     OBJ += $(AUDIO_OBJ) | ||||||
| else ifneq ($(MAKECMDGOALS), clean) | else ifneq ($(MAKECMDGOALS), clean) | ||||||
|   | |||||||
| @@ -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.8.4 | 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 | ||||||
| @@ -217,9 +237,24 @@ Audio output device\&. Integer value\&. Number corresponds to | |||||||
| /lsdev out | /lsdev out | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \fBVAD_treshold\fR | \fBVAD_threshold\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Voice Activity Detection treshold\&. Float value\&. Recommended values are around 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 | ||||||
|  |  | ||||||
| @@ -135,9 +147,18 @@ OPTIONS | |||||||
|     *output_device*;; |     *output_device*;; | ||||||
|         Audio output device. Integer value. Number corresponds to `/lsdev out` |         Audio output device. Integer value. Number corresponds to `/lsdev out` | ||||||
|  |  | ||||||
|     *VAD_treshold*;; |     *VAD_threshold*;; | ||||||
|         Voice Activity Detection treshold.  Float value. Recommended values are |         Voice Activity Detection threshold.  Float value. Recommended values are | ||||||
|         around 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; | ||||||
| @@ -82,8 +94,17 @@ audio = { | |||||||
|   // preferred audio output device; numbers correspond to /lsdev out |   // preferred audio output device; numbers correspond to /lsdev out | ||||||
|   output_device=0; |   output_device=0; | ||||||
|  |  | ||||||
|   // default VAD treshold; float (recommended values are around 40) |   // default VAD threshold; float (recommended values are 1.0-40.0) | ||||||
|   VAD_treshold=40.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; | ||||||
|             } |             } | ||||||
|   | |||||||
							
								
								
									
										824
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										824
									
								
								src/audio_call.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,7 +27,7 @@ | |||||||
|  |  | ||||||
| #include "audio_device.h" | #include "audio_device.h" | ||||||
|  |  | ||||||
| typedef enum _AudioError { | typedef enum AudioError { | ||||||
|     ae_None = 0, |     ae_None = 0, | ||||||
|     ae_StartingCaptureDevice = 1 << 0, |     ae_StartingCaptureDevice = 1 << 0, | ||||||
|     ae_StartingOutputDevice = 1 << 1, |     ae_StartingOutputDevice = 1 << 1, | ||||||
| @@ -35,7 +35,7 @@ typedef enum _AudioError { | |||||||
| } AudioError; | } AudioError; | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
| typedef enum _VideoError { | typedef enum VideoError { | ||||||
|     ve_None = 0, |     ve_None = 0, | ||||||
|     ve_StartingCaptureDevice = 1 << 0, |     ve_StartingCaptureDevice = 1 << 0, | ||||||
|     ve_StartingOutputDevice = 1 << 1, |     ve_StartingOutputDevice = 1 << 1, | ||||||
| @@ -44,14 +44,27 @@ typedef enum _VideoError { | |||||||
|  |  | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  | /* Status transitions: | ||||||
|  |  * None -> Pending (call invitation made or received); | ||||||
|  |  * Pending -> None (invitation rejected or failed); | ||||||
|  |  * Pending -> Active (call starts); | ||||||
|  |  * Active -> None (call ends). | ||||||
|  |  */ | ||||||
|  | typedef enum CallStatus { | ||||||
|  |     cs_None = 0, | ||||||
|  |     cs_Pending, | ||||||
|  |     cs_Active | ||||||
|  | } CallStatus; | ||||||
|  |  | ||||||
| typedef struct Call { | typedef struct Call { | ||||||
|     pthread_t ttid; /* Transmission thread id */ |     CallStatus status; | ||||||
|     bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */ |     uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */ | ||||||
|     uint32_t in_idx, out_idx; /* Audio Index */ |     uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */ | ||||||
| #ifdef VIDEO |     uint32_t audio_bit_rate; /* Bit rate for sending audio */ | ||||||
|     uint32_t vin_idx, vout_idx; /* Video Index */ |  | ||||||
| #endif /* VIDEO */ |     uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */ | ||||||
|     pthread_mutex_t mutex; |     uint32_t video_width, video_height; | ||||||
|  |     uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */ | ||||||
| } Call; | } Call; | ||||||
|  |  | ||||||
| struct CallControl { | struct CallControl { | ||||||
| @@ -66,19 +79,17 @@ struct CallControl { | |||||||
|     Call *calls; |     Call *calls; | ||||||
|     uint32_t max_calls; |     uint32_t max_calls; | ||||||
|  |  | ||||||
|     uint32_t call_state; |  | ||||||
|     bool pending_call; |  | ||||||
|     bool audio_enabled; |     bool audio_enabled; | ||||||
|     bool video_enabled; |     bool video_enabled; | ||||||
|  |  | ||||||
|     uint32_t audio_bit_rate; |  | ||||||
|     int32_t audio_frame_duration; |     int32_t audio_frame_duration; | ||||||
|     uint32_t audio_sample_rate; |     uint32_t audio_sample_rate; | ||||||
|     uint8_t audio_channels; |     uint8_t audio_channels; | ||||||
|  |     uint32_t default_audio_bit_rate; | ||||||
|  |  | ||||||
|     uint32_t video_bit_rate; |  | ||||||
|     int32_t video_frame_duration; |     int32_t video_frame_duration; | ||||||
|  |     uint32_t default_video_width, default_video_height; | ||||||
|  |     uint32_t default_video_bit_rate; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| extern struct CallControl CallControl; | extern struct CallControl CallControl; | ||||||
| @@ -86,9 +97,12 @@ extern struct CallControl CallControl; | |||||||
| /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | ||||||
| ToxAV *init_audio(ToxWindow *self, Tox *tox); | ToxAV *init_audio(ToxWindow *self, Tox *tox); | ||||||
| void terminate_audio(void); | void terminate_audio(void); | ||||||
| int start_transmission(ToxWindow *self, Call *call); |  | ||||||
| int stop_transmission(Call *call, uint32_t friend_number); | bool init_call(Call *call); | ||||||
|  |  | ||||||
|  | void place_call(ToxWindow *self); | ||||||
| void stop_current_call(ToxWindow *self); | void stop_current_call(ToxWindow *self); | ||||||
|  |  | ||||||
| void init_friend_AV(uint32_t index); | void init_friend_AV(uint32_t index); | ||||||
| void del_friend_AV(uint32_t index); | void del_friend_AV(uint32_t index); | ||||||
|  |  | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -30,6 +30,7 @@ | |||||||
| #define AUDIO_DEVICE_H | #define AUDIO_DEVICE_H | ||||||
|  |  | ||||||
| #define OPENAL_BUFS 5 | #define OPENAL_BUFS 5 | ||||||
|  | #define MAX_OPENAL_DEVICES 32 | ||||||
| #define MAX_DEVICES 32 | #define MAX_DEVICES 32 | ||||||
|  |  | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| @@ -55,42 +56,42 @@ typedef enum DeviceError { | |||||||
| typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data); | typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data); | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifdef AUDIO |  | ||||||
| DeviceError init_devices(ToxAV *av); |  | ||||||
| #else |  | ||||||
| DeviceError init_devices(void); | DeviceError init_devices(void); | ||||||
| #endif /* AUDIO */ |  | ||||||
|  |  | ||||||
| void get_devices_names(void); | void get_al_device_names(void); | ||||||
| DeviceError terminate_devices(void); | DeviceError terminate_devices(void); | ||||||
|  |  | ||||||
| /* Callback handles ready data from INPUT device */ |  | ||||||
| DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, |  | ||||||
|                                      void *data, bool enable_VAD); |  | ||||||
| void *get_device_callback_data(uint32_t device_idx); |  | ||||||
|  |  | ||||||
| /* toggle device mute */ | /* toggle device mute */ | ||||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx); | DeviceError device_mute(DeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
| #ifdef AUDIO | bool device_is_muted(DeviceType type, uint32_t device_idx); | ||||||
| DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); |  | ||||||
| #endif | DeviceError device_set_VAD_threshold(uint32_t device_idx, float value); | ||||||
|  |  | ||||||
|  | float device_get_VAD_threshold(uint32_t device_idx); | ||||||
|  |  | ||||||
|  | DeviceError set_source_position(uint32_t device_idx, float x, float y, float z); | ||||||
|  |  | ||||||
|  | DeviceError set_al_device(DeviceType type, int32_t selection); | ||||||
|  |  | ||||||
| DeviceError set_primary_device(DeviceType type, int32_t selection); |  | ||||||
| DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration, |  | ||||||
|                                 uint8_t channels); |  | ||||||
| /* Start device */ | /* Start device */ | ||||||
| DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate, | DeviceError open_input_device(uint32_t *device_idx, | ||||||
|                         uint32_t frame_duration, uint8_t channels); |                               DataHandleCallback cb, void *cb_data, bool enable_VAD, | ||||||
|  |                               uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); | ||||||
|  | DeviceError open_output_device(uint32_t *device_idx, | ||||||
|  |                                uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); | ||||||
|  |  | ||||||
| /* Stop device */ | /* Stop device */ | ||||||
| DeviceError close_device(DeviceType type, uint32_t device_idx); | DeviceError close_device(DeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
| /* Write data to device */ | /* Write data to output device */ | ||||||
| DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, | DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, | ||||||
|                       uint32_t sample_rate); |                       uint32_t sample_rate); | ||||||
|  |  | ||||||
| void print_devices(ToxWindow *self, DeviceType type); | /* return current input volume as float in range 0.0-100.0 */ | ||||||
| void get_primary_device_name(DeviceType type, char *buf, int size); | float get_input_volume(void); | ||||||
|  |  | ||||||
|  | void print_al_devices(ToxWindow *self, DeviceType type); | ||||||
|  |  | ||||||
| DeviceError selection_valid(DeviceType type, int32_t selection); | DeviceError selection_valid(DeviceType type, int32_t selection); | ||||||
| #endif /* AUDIO_DEVICE_H */ | #endif /* AUDIO_DEVICE_H */ | ||||||
|   | |||||||
| @@ -20,9 +20,9 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <limits.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <limits.h> |  | ||||||
|  |  | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| @@ -31,27 +31,24 @@ | |||||||
| #include <dirent.h> | #include <dirent.h> | ||||||
| #endif /* __APPLE__ */ | #endif /* __APPLE__ */ | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
|  | #include "execute.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| static void print_matches(ToxWindow *self, Tox *m, const void *list, size_t n_items, size_t size) | static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matches) | ||||||
| { | { | ||||||
|     if (m) { |     if (m) { | ||||||
|         execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); |         execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *L = (char *) list; |     for (size_t i = 0; i < n_matches; ++i) { | ||||||
|     int i; |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]); | ||||||
|  |  | ||||||
|     for (i = 0; i < n_items; ++i) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");   /* formatting */ |     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. | ||||||
| @@ -59,21 +56,19 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, size_t n_it | |||||||
|  * |  * | ||||||
|  * Returns the length of the match. |  * Returns the length of the match. | ||||||
|  */ |  */ | ||||||
| static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n) | static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, const char **matches, size_t n_items, | ||||||
|  |                             size_t max_size) | ||||||
| { | { | ||||||
|     UNUSED_VAR(self); |     UNUSED_VAR(self); | ||||||
|  |  | ||||||
|     if (n == 1) { |     if (n_items == 1) { | ||||||
|         return snprintf(match, match_sz, "%s", matches[0]); |         return snprintf(match, match_sz, "%s", matches[0]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int i; |     for (size_t i = 0; i < max_size; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_STR_SIZE; ++i) { |  | ||||||
|         char ch1 = matches[0][i]; |         char ch1 = matches[0][i]; | ||||||
|         int j; |  | ||||||
|  |  | ||||||
|         for (j = 0; j < n; ++j) { |         for (size_t j = 0; j < n_items; ++j) { | ||||||
|             char ch2 = matches[j][i]; |             char ch2 = matches[j][i]; | ||||||
|  |  | ||||||
|             if (ch1 != ch2 || !ch1) { |             if (ch1 != ch2 || !ch1) { | ||||||
| @@ -92,8 +87,7 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char | |||||||
|  * then fills line with the complete word. e.g. "Hello jo" would complete the line |  * then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||||
|  * with "Hello john". If multiple matches, prints out all the matches and semi-completes line. |  * with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||||
|  * |  * | ||||||
|  * list is a pointer to the list of strings being compared, n_items is the number of items |  * `list` is a pointer to `n_items` strings. Each string in the list must be <= MAX_STR_SIZE. | ||||||
|  * in the list, and size is the size of each item in the list. |  | ||||||
|  * |  * | ||||||
|  * dir_search should be true if the line being completed is a file path. |  * dir_search should be true if the line being completed is a file path. | ||||||
|  * |  * | ||||||
| @@ -102,7 +96,7 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char | |||||||
|  * |  * | ||||||
|  * Note: This function should not be called directly. Use complete_line() and complete_path() instead. |  * Note: This function should not be called directly. Use complete_line() and complete_path() instead. | ||||||
|  */ |  */ | ||||||
| static int complete_line_helper(ToxWindow *self, const void *list, size_t n_items, size_t size, bool dir_search) | static int complete_line_helper(ToxWindow *self, const char **list, const size_t n_items, bool dir_search) | ||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
| @@ -110,13 +104,12 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) { |     if (ctx->len >= MAX_STR_SIZE) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *L = (const char *) list; |  | ||||||
|     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) { | ||||||
| @@ -125,8 +118,8 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item | |||||||
|  |  | ||||||
|     /* 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); | ||||||
| @@ -154,38 +147,43 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!sub[0]) { |     if (!sub[0] && !(dir_search && n_items == 1)) { | ||||||
|         free(sub); |         free(sub); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int s_len = strlen(sub); |     int s_len = strlen(sub); | ||||||
|     size_t n_matches = 0; |     size_t n_matches = 0; | ||||||
|     char matches[n_items][MAX_STR_SIZE]; |  | ||||||
|     int i = 0; |     char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE); | ||||||
|  |  | ||||||
|  |     if (matches == NULL) { | ||||||
|  |         free(sub); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* put all list matches in matches array */ |     /* put all list matches in matches array */ | ||||||
|     for (i = 0; i < n_items; ++i) { |     for (size_t i = 0; i < n_items; ++i) { | ||||||
|         char str[MAX_CMDNAME_SIZE + 1]; |         if (strncasecmp(list[i], sub, s_len) == 0) { | ||||||
|         snprintf(str, sizeof(str), "%s", &L[i * size]); |             snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]); | ||||||
|  |  | ||||||
|         if (strncasecmp(str, sub, s_len) == 0) { |  | ||||||
|             strcpy(matches[n_matches++], str); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     free(sub); |     free(sub); | ||||||
|  |  | ||||||
|     if (!n_matches) { |     if (!n_matches) { | ||||||
|  |         free_ptr_array((void **) matches); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!dir_search && n_matches > 1) { |     if (!dir_search && n_matches > 1) { | ||||||
|         print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); |         print_ac_matches(self, NULL, matches, n_matches); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char match[MAX_STR_SIZE]; |     char match[MAX_STR_SIZE]; | ||||||
|     size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches); |     size_t match_len = get_str_match(self, match, sizeof(match), (const char **) matches, n_matches, MAX_STR_SIZE); | ||||||
|  |  | ||||||
|  |     free_ptr_array((void **) matches); | ||||||
|  |  | ||||||
|     if (match_len == 0) { |     if (match_len == 0) { | ||||||
|         return 0; |         return 0; | ||||||
| @@ -261,14 +259,14 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item | |||||||
|     return diff; |     return diff; | ||||||
| } | } | ||||||
|  |  | ||||||
| int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size) | int complete_line(ToxWindow *self, const char **list, size_t n_items) | ||||||
| { | { | ||||||
|     return complete_line_helper(self, list, n_items, size, false); |     return complete_line_helper(self, list, n_items, false); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int complete_path(ToxWindow *self, const void *list, size_t n_items, size_t size) | static int complete_path(ToxWindow *self, const char **list, const size_t n_items) | ||||||
| { | { | ||||||
|     return complete_line_helper(self, list, n_items, size, true); |     return complete_line_helper(self, list, n_items, true); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Transforms a tab complete starting with the shorthand "~" into the full home directory. */ | /* Transforms a tab complete starting with the shorthand "~" into the full home directory. */ | ||||||
| @@ -279,7 +277,7 @@ static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const c | |||||||
|     char homedir[MAX_STR_SIZE] = {0}; |     char homedir[MAX_STR_SIZE] = {0}; | ||||||
|     get_home_dir(homedir, sizeof(homedir)); |     get_home_dir(homedir, sizeof(homedir)); | ||||||
|  |  | ||||||
|     char newline[MAX_STR_SIZE]; |     char newline[MAX_STR_SIZE + 1]; | ||||||
|     snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1); |     snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1); | ||||||
|     snprintf(path, pathsize, "%s", &newline[cmdlen - 1]); |     snprintf(path, pathsize, "%s", &newline[cmdlen - 1]); | ||||||
|  |  | ||||||
| @@ -300,21 +298,33 @@ static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const c | |||||||
|     ctx->len = ctx->pos; |     ctx->len = ctx->pos; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Return true if the first `p_len` chars in `s` are equal to `p` and `s` is a valid directory name. | ||||||
|  |  */ | ||||||
|  | static bool is_partial_match(const char *s, const char *p, size_t p_len) | ||||||
|  | { | ||||||
|  |     if (s == NULL || p == NULL) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Attempts to match /command "<incomplete-dir>" line to matching directories. | /* Attempts to match /command "<incomplete-dir>" line to matching directories. | ||||||
|  * If there is only one match the line is auto-completed. |  * If there is only one match the line is auto-completed. | ||||||
|  * |  * | ||||||
|  * Returns the diff between old len and new len of ctx->line on success. |  * Returns the diff between old len and new len of ctx->line on success. | ||||||
|  * Returns -1 if no matches or more than one match. |  * Returns -1 if no matches or more than one match. | ||||||
|  */ |  */ | ||||||
| #define MAX_DIRS 512 | #define MAX_DIRS 75 | ||||||
| int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||||
| { | { | ||||||
|     char b_path[MAX_STR_SIZE]; |     char b_path[MAX_STR_SIZE + 1]; | ||||||
|     char b_name[MAX_STR_SIZE]; |     char b_name[MAX_STR_SIZE + 1]; | ||||||
|     char b_cmd[MAX_STR_SIZE]; |     char b_cmd[MAX_STR_SIZE]; | ||||||
|     const wchar_t *tmpline = &line[wcslen(cmd) + 1];   /* start after "/command " */ |     const wchar_t *tmpline = &line[wcslen(cmd) + 1];   /* start after "/command " */ | ||||||
|  |  | ||||||
|     if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) { |     if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path) - 1) == -1) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -323,7 +333,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (b_path[0] == '~') { |     if (b_path[0] == '~') { | ||||||
|         complete_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2); |         complete_home_dir(self, b_path, sizeof(b_path) - 1, b_cmd, strlen(b_cmd) + 2); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int si = char_rfind(b_path, '/', strlen(b_path)); |     int si = char_rfind(b_path, '/', strlen(b_path)); | ||||||
| @@ -332,28 +342,33 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | |||||||
|         b_path[0] = '.'; |         b_path[0] = '.'; | ||||||
|         b_path[1] = '\0'; |         b_path[1] = '\0'; | ||||||
|     } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */ |     } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */ | ||||||
|         char tmp[MAX_STR_SIZE]; |         memmove(b_path + 1, b_path, sizeof(b_path) - 1); | ||||||
|         snprintf(tmp, sizeof(tmp), ".%s", b_path); |         b_path[0] = '.'; | ||||||
|         snprintf(b_path, sizeof(b_path), "%s", tmp); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); |     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); | ||||||
|     b_path[si + 1] = '\0'; |     b_path[si + 1] = '\0'; | ||||||
|     int b_name_len = strlen(b_name); |     size_t b_name_len = strlen(b_name); | ||||||
|     DIR *dp = opendir(b_path); |     DIR *dp = opendir(b_path); | ||||||
|  |  | ||||||
|     if (dp == NULL) { |     if (dp == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char dirnames[MAX_DIRS][NAME_MAX + 1]; |     char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1); | ||||||
|  |  | ||||||
|  |     if (dirnames == NULL) { | ||||||
|  |         closedir(dp); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     struct dirent *entry; |     struct dirent *entry; | ||||||
|  |  | ||||||
|     int dircount = 0; |     int dircount = 0; | ||||||
|  |  | ||||||
|     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { |     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { | ||||||
|         if (strncmp(entry->d_name, b_name, b_name_len) == 0 |         if (is_partial_match(entry->d_name, b_name, b_name_len)) { | ||||||
|                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { |             snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name); | ||||||
|             snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); |  | ||||||
|             ++dircount; |             ++dircount; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -361,13 +376,18 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | |||||||
|     closedir(dp); |     closedir(dp); | ||||||
|  |  | ||||||
|     if (dircount == 0) { |     if (dircount == 0) { | ||||||
|  |         free_ptr_array((void **) dirnames); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (dircount > 1) { |     if (dircount > 1) { | ||||||
|         qsort(dirnames, dircount, NAME_MAX + 1, qsort_strcasecmp_hlpr); |         qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper); | ||||||
|         print_matches(self, m, dirnames, dircount, NAME_MAX + 1); |         print_ac_matches(self, m, dirnames, dircount); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return complete_path(self, dirnames, dircount, NAME_MAX + 1); |     int ret = complete_path(self, (const char **) dirnames, dircount); | ||||||
|  |  | ||||||
|  |     free_ptr_array((void **) dirnames); | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,18 +23,23 @@ | |||||||
| #ifndef AUTOCOMPLETE_H | #ifndef AUTOCOMPLETE_H | ||||||
| #define AUTOCOMPLETE_H | #define AUTOCOMPLETE_H | ||||||
|  |  | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Looks for all instances in list that begin with the last entered word in line according to pos, |  * Looks for all instances in list that begin with the last entered word in line according to pos, | ||||||
|  * then fills line with the complete word. e.g. "Hello jo" would complete the line |  * then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||||
|  * with "Hello john". If multiple matches, prints out all the matches and semi-completes line. |  * with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||||
|  * |  * | ||||||
|  * list is a pointer to the list of strings being compared, n_items is the number of items | * `list` is a pointer to `n_items` strings. | ||||||
|  * in the list, and size is the size of each item in the list. |  * | ||||||
|  |  * dir_search should be true if the line being completed is a file path. | ||||||
|  * |  * | ||||||
|  * Returns the difference between the old len and new len of line on success. |  * Returns the difference between the old len and new len of line on success. | ||||||
|  * Returns -1 on error. |  * Returns -1 on error. | ||||||
|  |  * | ||||||
|  |  * Note: This function should not be called directly. Use complete_line() and complete_path() instead. | ||||||
|  */ |  */ | ||||||
| int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size); | int complete_line(ToxWindow *self, const char **list, size_t n_items); | ||||||
|  |  | ||||||
| /* Attempts to match /command "<incomplete-dir>" line to matching directories. | /* Attempts to match /command "<incomplete-dir>" line to matching directories. | ||||||
|  * If there is only one match the line is auto-completed. |  * If there is only one match the line is auto-completed. | ||||||
|   | |||||||
| @@ -20,14 +20,14 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include "misc_tools.h" | #include "avatars.h" | ||||||
| #include "file_transfers.h" | #include "file_transfers.h" | ||||||
| #include "friendlist.h" | #include "friendlist.h" | ||||||
| #include "avatars.h" | #include "misc_tools.h" | ||||||
|  |  | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
|  |  | ||||||
| @@ -39,33 +39,66 @@ static struct Avatar { | |||||||
|     off_t size; |     off_t size; | ||||||
| } Avatar; | } Avatar; | ||||||
|  |  | ||||||
|  | /* Compares the first size bytes of fp to signature. | ||||||
|  |  * | ||||||
|  |  * Returns 0 if they are the same | ||||||
|  |  * Returns 1 if they differ | ||||||
|  |  * Returns -1 on error. | ||||||
|  |  * | ||||||
|  |  * On success this function will seek back to the beginning of fp. | ||||||
|  |  */ | ||||||
|  | static int check_file_signature(const unsigned char *signature, size_t size, FILE *fp) | ||||||
|  | { | ||||||
|  |     char *buf = malloc(size); | ||||||
|  |  | ||||||
|  |     if (buf == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fread(buf, size, 1, fp) != 1) { | ||||||
|  |         free(buf); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int ret = memcmp(signature, buf, size); | ||||||
|  |  | ||||||
|  |     free(buf); | ||||||
|  |  | ||||||
|  |     if (fseek(fp, 0L, SEEK_SET) == -1) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ret == 0 ? 0 : 1; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void avatar_clear(void) | static void avatar_clear(void) | ||||||
| { | { | ||||||
|     memset(&Avatar, 0, sizeof(struct Avatar)); |     Avatar = (struct Avatar) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Sends avatar to friendnum. | /* Sends avatar to friendnumber. | ||||||
|  * |  * | ||||||
|  * Returns 0 on success. |  * Returns 0 on success. | ||||||
|  * Returns -1 on failure. |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| int avatar_send(Tox *m, uint32_t friendnum) | int avatar_send(Tox *m, uint32_t friendnumber) | ||||||
| { | { | ||||||
|     Tox_Err_File_Send err; |     Tox_Err_File_Send err; | ||||||
|     uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, |     uint32_t filenumber = tox_file_send(m, friendnumber, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, | ||||||
|                                      NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); |                                         NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); | ||||||
|  |  | ||||||
|     if (Avatar.size == 0) { |     if (Avatar.size == 0) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FILE_SEND_OK) { |     if (err != TOX_ERR_FILE_SEND_OK) { | ||||||
|         fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err); |         fprintf(stderr, "tox_file_send failed for friendnumber %u (error %d)\n", friendnumber, err); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); |     struct FileTransfer *ft = new_file_transfer(NULL, friendnumber, filenumber, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); | ||||||
|  |  | ||||||
|     if (!ft) { |     if (!ft) { | ||||||
|         return -1; |         return -1; | ||||||
| @@ -86,9 +119,7 @@ int avatar_send(Tox *m, uint32_t friendnum) | |||||||
| /* Sends avatar to all friends */ | /* Sends avatar to all friends */ | ||||||
| static void avatar_send_all(Tox *m) | static void avatar_send_all(Tox *m) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < Friends.max_idx; ++i) { |  | ||||||
|         if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) { |         if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) { | ||||||
|             avatar_send(m, Friends.list[i].num); |             avatar_send(m, Friends.list[i].num); | ||||||
|         } |         } | ||||||
| @@ -149,6 +180,13 @@ void avatar_unset(Tox *m) | |||||||
|     avatar_send_all(m); |     avatar_send_all(m); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status) | ||||||
|  | { | ||||||
|  |     if (connection_status == TOX_CONNECTION_NONE) { | ||||||
|  |         kill_avatar_file_transfers_friend(m, friendnumber); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control) | void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control) | ||||||
| { | { | ||||||
|     switch (control) { |     switch (control) { | ||||||
| @@ -196,21 +234,29 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, | |||||||
|         ft->position = position; |         ft->position = position; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t send_data[length]; |     uint8_t *send_data = malloc(length); | ||||||
|     size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file); |  | ||||||
|  |  | ||||||
|     if (send_length != length) { |     if (send_data == NULL) { | ||||||
|         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); |         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     size_t send_length = fread(send_data, 1, length, ft->file); | ||||||
|  |  | ||||||
|  |     if (send_length != length) { | ||||||
|  |         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|  |         free(send_data); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     Tox_Err_File_Send_Chunk err; |     Tox_Err_File_Send_Chunk err; | ||||||
|     tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err); |     tox_file_send_chunk(m, ft->friendnumber, ft->filenumber, position, send_data, send_length, &err); | ||||||
|  |  | ||||||
|  |     free(send_data); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FILE_SEND_CHUNK_OK) { |     if (err != TOX_ERR_FILE_SEND_CHUNK_OK) { | ||||||
|         fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); |         fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ft->position += send_length; |     ft->position += send_length; | ||||||
|     ft->last_keep_alive = get_unix_time(); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,6 +23,8 @@ | |||||||
| #ifndef AVATARS_H | #ifndef AVATARS_H | ||||||
| #define AVATARS_H | #define AVATARS_H | ||||||
|  |  | ||||||
|  | #include "file_transfers.h" | ||||||
|  |  | ||||||
| #define MAX_AVATAR_FILE_SIZE 65536 | #define MAX_AVATAR_FILE_SIZE 65536 | ||||||
|  |  | ||||||
| /* Sends avatar to friendnum. | /* Sends avatar to friendnum. | ||||||
| @@ -48,5 +50,6 @@ void avatar_unset(Tox *m); | |||||||
|  |  | ||||||
| void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length); | void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length); | ||||||
| void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control); | void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control); | ||||||
|  | void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status); | ||||||
|  |  | ||||||
| #endif /* AVATARS_H */ | #endif /* AVATARS_H */ | ||||||
|   | |||||||
| @@ -20,21 +20,24 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <stdbool.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdbool.h> | #include <sys/socket.h> | ||||||
| #include <limits.h> |  | ||||||
|  |  | ||||||
| #include <curl/curl.h> | #include <curl/curl.h> | ||||||
| #include <tox/tox.h> | #include <tox/tox.h> | ||||||
|  |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
| #include "curl_util.h" | #include "curl_util.h" | ||||||
| #include "settings.h" | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
| #include "prompt.h" | #include "prompt.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern struct arg_opts arg_opts; | extern struct arg_opts arg_opts; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
| @@ -105,6 +108,37 @@ static struct DHT_Nodes { | |||||||
|     time_t last_updated; |     time_t last_updated; | ||||||
| } Nodes; | } Nodes; | ||||||
|  |  | ||||||
|  | /* Return true if address appears to be a valid ipv4 address. */ | ||||||
|  | static bool is_ip4_address(const char *address) | ||||||
|  | { | ||||||
|  |     struct sockaddr_in s_addr; | ||||||
|  |     return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return true if address roughly appears to be a valid ipv6 address. | ||||||
|  |  * | ||||||
|  |  * TODO: Improve this function (inet_pton behaves strangely with ipv6). | ||||||
|  |  * for now the only guarantee is that it won't return true if the | ||||||
|  |  * address is a domain or ipv4 address, and should only be used if you're | ||||||
|  |  * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). | ||||||
|  |  */ | ||||||
|  | static bool is_ip6_address(const char *address) | ||||||
|  | { | ||||||
|  |     size_t num_colons = 0; | ||||||
|  |     char ch = 0; | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; (ch = address[i]); ++i) { | ||||||
|  |         if (ch == '.') { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (ch == ':') { | ||||||
|  |             ++num_colons; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return num_colons > 1 && num_colons < 8; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Determine if a node is offline by comparing the age of the nodeslist | /* Determine if a node is offline by comparing the age of the nodeslist | ||||||
|  * to the last time the node was successfully pinged. |  * to the last time the node was successfully pinged. | ||||||
| @@ -241,6 +275,7 @@ on_exit: | |||||||
|  * Return -2 if http lookup failed. |  * Return -2 if http lookup failed. | ||||||
|  * Return -3 if http reponse was empty. |  * Return -3 if http reponse was empty. | ||||||
|  * Return -4 if data could not be written to disk. |  * Return -4 if data could not be written to disk. | ||||||
|  |  * Return -5 if memory allocation fails. | ||||||
|  */ |  */ | ||||||
| static int update_DHT_nodeslist(const char *nodes_path) | static int update_DHT_nodeslist(const char *nodes_path) | ||||||
| { | { | ||||||
| @@ -254,26 +289,34 @@ static int update_DHT_nodeslist(const char *nodes_path) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct Recv_Curl_Data recv_data; |     struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data)); | ||||||
|  |  | ||||||
|     memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); |     if (recv_data == NULL) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return -5; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (curl_fetch_nodes_JSON(&recv_data) == -1) { |     if (curl_fetch_nodes_JSON(recv_data) == -1) { | ||||||
|  |         free(recv_data); | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         return -2; |         return -2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (recv_data.length == 0) { |     if (recv_data->length == 0) { | ||||||
|  |         free(recv_data); | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         return -3; |         return -3; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) { |     if (fwrite(recv_data->data, recv_data->length, 1, fp) != 1) { | ||||||
|  |         free(recv_data); | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         return -4; |         return -4; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     free(recv_data); | ||||||
|     fclose(fp); |     fclose(fp); | ||||||
|  |  | ||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										600
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										600
									
								
								src/chat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -23,8 +23,8 @@ | |||||||
| #ifndef CHAT_H | #ifndef CHAT_H | ||||||
| #define CHAT_H | #define CHAT_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| /* set CTRL to -1 if we don't want to send a control signal. | /* set CTRL to -1 if we don't want to send a control signal. | ||||||
|    set msg to NULL if we don't want to display a message */ |    set msg to NULL if we don't want to display a message */ | ||||||
|   | |||||||
| @@ -23,15 +23,15 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "chat.h" | ||||||
|  | #include "conference.h" | ||||||
|  | #include "execute.h" | ||||||
|  | #include "file_transfers.h" | ||||||
|  | #include "friendlist.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "friendlist.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "groupchat.h" |  | ||||||
| #include "chat.h" |  | ||||||
| #include "file_transfers.h" |  | ||||||
|  |  | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
| @@ -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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -80,72 +80,96 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); |     close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int groupnum = strtol(argv[1], NULL, 10); |     long int conferencenum = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == 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 group 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, groupnum, &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 group (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 Group %d.", groupnum); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_join_group(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]) | ||||||
| { | { | ||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|     UNUSED_VAR(argc); |     UNUSED_VAR(argc); | ||||||
|     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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *groupkey = Friends.list[self->num].group_invite.key; |     const char *conferencekey = Friends.list[self->num].conference_invite.key; | ||||||
|     uint16_t length = Friends.list[self->num].group_invite.length; |     uint16_t length = Friends.list[self->num].conference_invite.length; | ||||||
|     uint8_t type = Friends.list[self->num].group_invite.type; |     uint8_t type = Friends.list[self->num].conference_invite.type; | ||||||
|  |  | ||||||
|     if (!Friends.list[self->num].group_invite.pending) { |     if (!Friends.list[self->num].conference_invite.pending) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (type != TOX_CONFERENCE_TYPE_TEXT) { |     uint32_t conferencenum; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups."); |  | ||||||
|  |     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||||
|  |         Tox_Err_Conference_Join err; | ||||||
|  |         conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_CONFERENCE_JOIN_OK) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } else if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
|  | #ifdef AUDIO | ||||||
|  |         conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length, | ||||||
|  |                                                 audio_conference_callback, NULL); | ||||||
|  |  | ||||||
|  |         if (conferencenum == (uint32_t) -1) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | #else | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||||
|  |         return; | ||||||
|  | #endif | ||||||
|  |     } else { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Tox_Err_Conference_Join err; |     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||||
|     uint32_t groupnum = tox_conference_join(m, self->num, (const uint8_t *) groupkey, length, &err); |         tox_conference_delete(m, conferencenum, NULL); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_CONFERENCE_JOIN_OK) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) { | #ifdef AUDIO | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); |  | ||||||
|         tox_conference_delete(m, groupnum, NULL); |     if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
|         return; |         if (!init_conference_audio_input(m, conferencenum)) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -153,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -183,18 +207,18 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     Tox_Err_File_Control err; |     Tox_Err_File_Control err; | ||||||
|     tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err); |     tox_file_control(m, self->num, ft->filenumber, TOX_FILE_CONTROL_RESUME, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FILE_CONTROL_OK) { |     if (err != TOX_ERR_FILE_CONTROL_OK) { | ||||||
|         goto on_recv_error; |         goto on_recv_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] 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; | ||||||
| @@ -205,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; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -233,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -242,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; | ||||||
|     } |     } | ||||||
| @@ -286,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; | ||||||
|  |  | ||||||
| @@ -314,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); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,12 +23,12 @@ | |||||||
| #ifndef CHAT_COMMANDS_H | #ifndef CHAT_COMMANDS_H | ||||||
| #define CHAT_COMMANDS_H | #define CHAT_COMMANDS_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  |  | ||||||
| @@ -45,8 +45,9 @@ void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SI | |||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|  | void cmd_vcall(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_res(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|  |  | ||||||
| #endif /* CHAT_COMMANDS_H */ | #endif /* CHAT_COMMANDS_H */ | ||||||
|   | |||||||
							
								
								
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | /*  conference.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  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/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef CONFERENCE_H | ||||||
|  | #define CONFERENCE_H | ||||||
|  |  | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
|  | #define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH | ||||||
|  | #define SIDEBAR_WIDTH 16 | ||||||
|  |  | ||||||
|  | typedef struct ConferencePeer { | ||||||
|  |     bool       active; | ||||||
|  |  | ||||||
|  |     uint8_t    pubkey[TOX_PUBLIC_KEY_SIZE]; | ||||||
|  |     uint32_t   peernum;    /* index in chat->peer_list */ | ||||||
|  |  | ||||||
|  |     char       name[TOX_MAX_NAME_LENGTH]; | ||||||
|  |     size_t     name_length; | ||||||
|  |  | ||||||
|  |     bool       sending_audio; | ||||||
|  |     uint32_t   audio_out_idx; | ||||||
|  |     time_t     last_audio_time; | ||||||
|  | } ConferencePeer; | ||||||
|  |  | ||||||
|  | typedef struct AudioInputCallbackData { | ||||||
|  |     Tox *tox; | ||||||
|  |     uint32_t conferencenum; | ||||||
|  | } AudioInputCallbackData; | ||||||
|  |  | ||||||
|  | #define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1) | ||||||
|  | typedef struct NameListEntry { | ||||||
|  |     char name[TOX_MAX_NAME_LENGTH]; | ||||||
|  |     char pubkey_str[PUBKEY_STRING_SIZE]; | ||||||
|  |     uint32_t peernum; | ||||||
|  | } NameListEntry; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     int chatwin; | ||||||
|  |     bool active; | ||||||
|  |     uint8_t type; | ||||||
|  |     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ | ||||||
|  |     time_t start_time; | ||||||
|  |  | ||||||
|  |     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||||
|  |     size_t title_length; | ||||||
|  |  | ||||||
|  |     ConferencePeer *peer_list; | ||||||
|  |     uint32_t max_idx; | ||||||
|  |  | ||||||
|  |     NameListEntry *name_list; | ||||||
|  |     uint32_t num_peers; | ||||||
|  |  | ||||||
|  |     bool push_to_talk_enabled; | ||||||
|  |     time_t ptt_last_pushed; | ||||||
|  |  | ||||||
|  |     bool audio_enabled; | ||||||
|  |     time_t last_sent_audio; | ||||||
|  |     uint32_t audio_in_idx; | ||||||
|  |     AudioInputCallbackData audio_input_callback_data; | ||||||
|  | } ConferenceChat; | ||||||
|  |  | ||||||
|  | /* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */ | ||||||
|  | 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 length); | ||||||
|  |  | ||||||
|  | /* destroys and re-creates conference window with or without the peerlist */ | ||||||
|  | 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 | ||||||
|  |  * of `maxpeers`. | ||||||
|  |  * Maches each peer whose name or pubkey begins with `prefix`. | ||||||
|  |  * If `prefix` is exactly the pubkey of a peer, matches only that peer. | ||||||
|  |  * return number of entries placed in `entries`. | ||||||
|  |  */ | ||||||
|  | uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries, | ||||||
|  |         uint32_t maxpeers); | ||||||
|  |  | ||||||
|  | bool init_conference_audio_input(Tox *tox, uint32_t conferencenum); | ||||||
|  | bool enable_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, | ||||||
|  |                                const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t | ||||||
|  |                                sample_rate, void *userdata); | ||||||
|  |  | ||||||
|  | bool conference_mute_self(uint32_t conferencenum); | ||||||
|  | bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum); | ||||||
|  | bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold); | ||||||
|  | float conference_get_VAD_threshold(uint32_t conferencenum); | ||||||
|  |  | ||||||
|  | #endif /* CONFERENCE_H */ | ||||||
							
								
								
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | /*  conference_commands.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 <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "conference.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
|  | static void print_err(ToxWindow *self, const char *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]) | ||||||
|  | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|  |     Tox_Err_Conference_Title err; | ||||||
|  |     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||||
|  |  | ||||||
|  |     if (argc < 1) { | ||||||
|  |         size_t tlen = tox_conference_get_title_size(m, self->num, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) { | ||||||
|  |             print_err(self, "Title is not set"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) { | ||||||
|  |             print_err(self, "Title is not set"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         title[tlen] = '\0'; | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); | ||||||
|  |  | ||||||
|  |         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]); | ||||||
|  |  | ||||||
|  |     if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     conference_rename_log_path(m, self->num, title);  // must be called first | ||||||
|  |  | ||||||
|  |     conference_set_title(self, self->num, title, len); | ||||||
|  |  | ||||||
|  |     char selfnick[TOX_MAX_NAME_LENGTH]; | ||||||
|  |     tox_self_get_name(m, (uint8_t *) selfnick); | ||||||
|  |  | ||||||
|  |     size_t sn_len = tox_self_get_name_size(m); | ||||||
|  |     selfnick[sn_len] = '\0'; | ||||||
|  |  | ||||||
|  |     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]; | ||||||
|  |     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); | ||||||
|  |     write_to_log(tmp_event, selfnick, self->chatwin->log, true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  | void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|  |     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 (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) { | ||||||
|  |         print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk." | ||||||
|  |                   : "Disabled conference audio"); | ||||||
|  |     } else { | ||||||
|  |         print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|  |     if (argc < 1) { | ||||||
|  |         if (conference_mute_self(self->num)) { | ||||||
|  |             print_err(self, "Toggled self audio mute status"); | ||||||
|  |         } else { | ||||||
|  |             print_err(self, "No audio input to mute"); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         NameListEntry *entries[16]; | ||||||
|  |         uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16); | ||||||
|  |  | ||||||
|  |         if (n == 0) { | ||||||
|  |             print_err(self, "No such peer"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (n > 1) { | ||||||
|  |             print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):"); | ||||||
|  |  | ||||||
|  |             for (uint32_t i = 0; i < n; ++i) { | ||||||
|  |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (conference_mute_peer(m, self->num, entries[0]->peernum)) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name); | ||||||
|  |         } else { | ||||||
|  |             print_err(self, "Peer is not on the call"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|  |     if (argc == 0) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f", | ||||||
|  |                       (double) conference_get_VAD_threshold(self->num)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (argc > 1) { | ||||||
|  |         print_err(self, "Only one argument allowed."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char *end; | ||||||
|  |     float value = strtof(argv[1], &end); | ||||||
|  |  | ||||||
|  |     if (*end) { | ||||||
|  |         print_err(self, "Invalid input"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (conference_set_VAD_threshold(self->num, value)) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value); | ||||||
|  |     } else { | ||||||
|  |         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 */ | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| /*  xtra.h
 | /*  conference_commands.h
 | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
| @@ -20,21 +20,16 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #ifndef XTRA_H | #ifndef CONFERENCE_COMMANDS_H | ||||||
| #define XTRA_H | #define CONFERENCE_COMMANDS_H | ||||||
| 
 | 
 | ||||||
| /* NOTE: If no xlib present don't compile */ | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { | void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|     DT_plain, | void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|     DT_file_list | 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]); | ||||||
| DropType; | void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| 
 | 
 | ||||||
| typedef void (*drop_callback)(const char *, DropType); | #endif /* CONFERENCE_COMMANDS_H */ | ||||||
| 
 |  | ||||||
| int               init_xtra(drop_callback d); |  | ||||||
| void              terminate_xtra(void); |  | ||||||
| int               is_focused(void); /* returns bool */ |  | ||||||
| 
 |  | ||||||
| #endif /* XTRA_H */ |  | ||||||
| @@ -20,18 +20,18 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/stat.h> |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <unistd.h> |  | ||||||
| #include <pwd.h> | #include <pwd.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
|  | #include "toxic.h" | ||||||
|  |  | ||||||
| /* get the user's home directory. */ | /* get the user's home directory. */ | ||||||
| void get_home_dir(char *home, int size) | void get_home_dir(char *home, int size) | ||||||
|   | |||||||
| @@ -23,6 +23,8 @@ | |||||||
| #ifndef CURL_UTIL_H | #ifndef CURL_UTIL_H | ||||||
| #define CURL_UTIL_H | #define CURL_UTIL_H | ||||||
|  |  | ||||||
|  | #include <stdint.h> | ||||||
|  |  | ||||||
| /* List based on Mozilla's recommended configurations for modern browsers */ | /* List based on Mozilla's recommended configurations for modern browsers */ | ||||||
| #define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" | #define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,20 +20,20 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <assert.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <assert.h> |  | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "api.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "chat_commands.h" | #include "chat_commands.h" | ||||||
|  | #include "execute.h" | ||||||
| #include "global_commands.h" | #include "global_commands.h" | ||||||
| #include "group_commands.h" | #include "conference_commands.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
| #include "api.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| struct cmd_func { | struct cmd_func { | ||||||
|     const char *name; |     const char *name; | ||||||
| @@ -48,7 +48,7 @@ static struct cmd_func global_commands[] = { | |||||||
|     { "/connect",   cmd_connect       }, |     { "/connect",   cmd_connect       }, | ||||||
|     { "/decline",   cmd_decline       }, |     { "/decline",   cmd_decline       }, | ||||||
|     { "/exit",      cmd_quit          }, |     { "/exit",      cmd_quit          }, | ||||||
|     { "/group",     cmd_groupchat     }, |     { "/conference", cmd_conference    }, | ||||||
|     { "/help",      cmd_prompt_help   }, |     { "/help",      cmd_prompt_help   }, | ||||||
|     { "/log",       cmd_log           }, |     { "/log",       cmd_log           }, | ||||||
|     { "/myid",      cmd_myid          }, |     { "/myid",      cmd_myid          }, | ||||||
| @@ -67,8 +67,8 @@ static struct cmd_func global_commands[] = { | |||||||
|     { "/sdev",      cmd_change_device }, |     { "/sdev",      cmd_change_device }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|     { "/lsvdev",    cmd_list_video_devices }, |     { "/lsvdev",    cmd_list_video_devices  }, | ||||||
|     { "/svdev",    cmd_change_video_device }, |     { "/svdev",     cmd_change_video_device }, | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
|     { "/run",       cmd_run           }, |     { "/run",       cmd_run           }, | ||||||
| @@ -77,41 +77,45 @@ static struct cmd_func global_commands[] = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct cmd_func chat_commands[] = { | static struct cmd_func chat_commands[] = { | ||||||
|     { "/cancel",    cmd_cancelfile  }, |     { "/cancel",    cmd_cancelfile        }, | ||||||
|     { "/invite",    cmd_groupinvite }, |     { "/invite",    cmd_conference_invite }, | ||||||
|     { "/join",      cmd_join_group  }, |     { "/join",      cmd_conference_join   }, | ||||||
|     { "/savefile",  cmd_savefile    }, |     { "/savefile",  cmd_savefile          }, | ||||||
|     { "/sendfile",  cmd_sendfile    }, |     { "/sendfile",  cmd_sendfile          }, | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     { "/call",      cmd_call        }, |     { "/call",      cmd_call              }, | ||||||
|     { "/answer",    cmd_answer      }, |     { "/answer",    cmd_answer            }, | ||||||
|     { "/reject",    cmd_reject      }, |     { "/reject",    cmd_reject            }, | ||||||
|     { "/hangup",    cmd_hangup      }, |     { "/hangup",    cmd_hangup            }, | ||||||
|     { "/mute",      cmd_mute        }, |     { "/mute",      cmd_mute              }, | ||||||
|     { "/sense",     cmd_sense       }, |     { "/sense",     cmd_sense             }, | ||||||
|     { "/bitrate",   cmd_bitrate     }, |     { "/bitrate",   cmd_bitrate           }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|     { "/video",     cmd_video       }, |     { "/vcall",     cmd_vcall             }, | ||||||
|  |     { "/video",     cmd_video             }, | ||||||
|  |     { "/res",       cmd_res               }, | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|     { NULL,         NULL            }, |     { NULL,         NULL                  }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct cmd_func group_commands[] = { | static struct cmd_func conference_commands[] = { | ||||||
|     { "/title",     cmd_set_title   }, |     { "/title",     cmd_conference_set_title }, | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     { "/mute",      cmd_mute        }, |     { "/audio",     cmd_enable_audio }, | ||||||
|     { "/sense",     cmd_sense       }, |     { "/mute",      cmd_conference_mute   }, | ||||||
|  |     { "/ptt",       cmd_conference_push_to_talk }, | ||||||
|  |     { "/sense",     cmd_conference_sense  }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|     { NULL,         NULL            }, |     { NULL,         NULL             }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
| #define SPECIAL_COMMANDS 6 | #define SPECIAL_COMMANDS 7 | ||||||
| #else | #else | ||||||
| #define SPECIAL_COMMANDS 5 | #define SPECIAL_COMMANDS 6 | ||||||
| #endif /* PYTHON */ | #endif /* PYTHON */ | ||||||
|  |  | ||||||
| /* Special commands are commands that only take one argument even if it contains spaces */ | /* Special commands are commands that only take one argument even if it contains spaces */ | ||||||
| @@ -124,6 +128,7 @@ static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = { | |||||||
| #endif /* PYTHON */ | #endif /* PYTHON */ | ||||||
|     "/sendfile", |     "/sendfile", | ||||||
|     "/title", |     "/title", | ||||||
|  |     "/mute", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Returns true if input command is in the special_commands array. */ | /* Returns true if input command is in the special_commands array. */ | ||||||
| @@ -246,8 +251,8 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | |||||||
|  |  | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case GROUPCHAT_COMMAND_MODE: |         case CONFERENCE_COMMAND_MODE: | ||||||
|             if (do_command(w, self, m, num_args, group_commands, args) == 0) { |             if (do_command(w, self, m, num_args, conference_commands, args) == 0) { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -266,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."); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ | |||||||
| enum { | enum { | ||||||
|     GLOBAL_COMMAND_MODE, |     GLOBAL_COMMAND_MODE, | ||||||
|     CHAT_COMMAND_MODE, |     CHAT_COMMAND_MODE, | ||||||
|     GROUPCHAT_COMMAND_MODE, |     CONFERENCE_COMMAND_MODE, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | ||||||
|   | |||||||
| @@ -20,23 +20,24 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "friendlist.h" |  | ||||||
| #include "file_transfers.h" | #include "file_transfers.h" | ||||||
|  | #include "friendlist.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
|  |  | ||||||
| /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | ||||||
| #define NUM_PROG_MARKS 50 | #define NUM_PROG_MARKS 50 | ||||||
|  | #define STR_BUF_SIZE 30 | ||||||
|  |  | ||||||
| /* creates initial progress line that will be updated during file transfer. | /* creates initial progress line that will be updated during file transfer. | ||||||
|    Assumes progline has room for at least MAX_STR_SIZE bytes */ |    Assumes progline has room for at least MAX_STR_SIZE bytes */ | ||||||
| @@ -59,13 +60,15 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char pct_str[24]; |     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[24]; |     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; | ||||||
|  |  | ||||||
| @@ -81,10 +84,18 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l | |||||||
|         strcat(prog_line, "-"); |         strcat(prog_line, "-"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7]; |     size_t line_buf_size = strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7; | ||||||
|     snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str); |     char *full_line = malloc(line_buf_size); | ||||||
|  |  | ||||||
|  |     if (full_line == NULL) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(full_line, line_buf_size, "%s [%s] %s/s", pct_str, prog_line, bps_str); | ||||||
|  |  | ||||||
|     line_info_set(self, line_id, full_line); |     line_info_set(self, line_id, full_line); | ||||||
|  |  | ||||||
|  |     free(full_line); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft) | static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft) | ||||||
| @@ -107,33 +118,36 @@ static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* refreshes active file transfer status bars. */ | /* refreshes active file transfer status bars. */ | ||||||
| void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum) | void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         refresh_progress_helper(self, &Friends.list[friendnumber].file_receiver[i]); | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |         refresh_progress_helper(self, &Friends.list[friendnumber].file_sender[i]); | ||||||
|         refresh_progress_helper(self, &Friends.list[friendnum].file_receiver[i]); |  | ||||||
|         refresh_progress_helper(self, &Friends.list[friendnum].file_sender[i]); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. | static void clear_file_transfer(struct FileTransfer *ft) | ||||||
|  * Returns NULL if filenum is invalid. |  | ||||||
|  */ |  | ||||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum) |  | ||||||
| { | { | ||||||
|     size_t i; |     *ft = (struct FileTransfer) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { | /* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber. | ||||||
|         struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i]; |  * Returns NULL if filenumber is invalid. | ||||||
|  |  */ | ||||||
|  | struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber) | ||||||
|  | { | ||||||
|  |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i]; | ||||||
|  |  | ||||||
|         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) { |         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) { | ||||||
|             return ft_send; |             return ft_send; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i]; |         struct FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i]; | ||||||
|  |  | ||||||
|         if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) { |         if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) { | ||||||
|             return ft_recv; |             return ft_recv; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -144,19 +158,17 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen | |||||||
| /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index, | ||||||
|         FILE_TRANSFER_DIRECTION direction) |         FILE_TRANSFER_DIRECTION direction) | ||||||
| { | { | ||||||
|     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) { |     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     size_t i; |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |  | ||||||
|         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? |         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? | ||||||
|                                       &Friends.list[friendnum].file_sender[i] : |                                       &Friends.list[friendnumber].file_sender[i] : | ||||||
|                                       &Friends.list[friendnum].file_receiver[i]; |                                       &Friends.list[friendnumber].file_receiver[i]; | ||||||
|  |  | ||||||
|         if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) { |         if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) { | ||||||
|             return ft; |             return ft; | ||||||
| @@ -169,23 +181,19 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t | |||||||
| /* Returns a pointer to an unused file sender. | /* Returns a pointer to an unused file sender. | ||||||
|  * Returns NULL if all file senders are in use. |  * Returns NULL if all file senders are in use. | ||||||
|  */ |  */ | ||||||
| static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) | static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, uint8_t type) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |  | ||||||
|         struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i]; |  | ||||||
|  |  | ||||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { |         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|             memset(ft, 0, sizeof(struct FileTransfer)); |             clear_file_transfer(ft); | ||||||
|             ft->window = window; |             ft->window = window; | ||||||
|             ft->index = i; |             ft->index = i; | ||||||
|             ft->friendnum = friendnum; |             ft->friendnumber = friendnumber; | ||||||
|             ft->filenum = filenum; |             ft->filenumber = filenumber; | ||||||
|             ft->file_type = type; |             ft->file_type = type; | ||||||
|             ft->last_keep_alive = get_unix_time(); |  | ||||||
|             ft->state = FILE_TRANSFER_PENDING; |             ft->state = FILE_TRANSFER_PENDING; | ||||||
|             ft->direction = FILE_TRANSFER_SEND; |  | ||||||
|             return ft; |             return ft; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -196,23 +204,20 @@ static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnu | |||||||
| /* Returns a pointer to an unused file receiver. | /* Returns a pointer to an unused file receiver. | ||||||
|  * Returns NULL if all file receivers are in use. |  * Returns NULL if all file receivers are in use. | ||||||
|  */ |  */ | ||||||
| static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) | static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, | ||||||
|  |         uint8_t type) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i]; | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |  | ||||||
|         struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i]; |  | ||||||
|  |  | ||||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { |         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|             memset(ft, 0, sizeof(struct FileTransfer)); |             clear_file_transfer(ft); | ||||||
|             ft->window = window; |             ft->window = window; | ||||||
|             ft->index = i; |             ft->index = i; | ||||||
|             ft->friendnum = friendnum; |             ft->friendnumber = friendnumber; | ||||||
|             ft->filenum = filenum; |             ft->filenumber = filenumber; | ||||||
|             ft->file_type = type; |             ft->file_type = type; | ||||||
|             ft->last_keep_alive = get_unix_time(); |  | ||||||
|             ft->state = FILE_TRANSFER_PENDING; |             ft->state = FILE_TRANSFER_PENDING; | ||||||
|             ft->direction = FILE_TRANSFER_RECV; |  | ||||||
|             return ft; |             return ft; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -223,15 +228,15 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend | |||||||
| /* Initializes an unused file transfer and returns its pointer. | /* Initializes an unused file transfer and returns its pointer. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, | struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, | ||||||
|                                        FILE_TRANSFER_DIRECTION direction, uint8_t type) |                                        FILE_TRANSFER_DIRECTION direction, uint8_t type) | ||||||
| { | { | ||||||
|     if (direction == FILE_TRANSFER_RECV) { |     if (direction == FILE_TRANSFER_RECV) { | ||||||
|         return new_file_receiver(window, friendnum, filenum, type); |         return new_file_receiver(window, friendnumber, filenumber, type); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (direction == FILE_TRANSFER_SEND) { |     if (direction == FILE_TRANSFER_SEND) { | ||||||
|         return new_file_sender(window, friendnum, filenum, type); |         return new_file_sender(window, friendnumber, filenumber, type); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return NULL; |     return NULL; | ||||||
| @@ -259,7 +264,7 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (CTRL >= 0) { |     if (CTRL >= 0) { | ||||||
|         tox_file_control(m, ft->friendnum, ft->filenum, (Tox_File_Control) CTRL, NULL); |         tox_file_control(m, ft->friendnumber, ft->filenumber, (Tox_File_Control) CTRL, NULL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (message && self) { |     if (message && self) { | ||||||
| @@ -269,28 +274,36 @@ 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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(ft, 0, sizeof(struct FileTransfer)); |     clear_file_transfer(ft); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Kills all active file transfers for friendnum */ | /* Kills active outgoing avatar file transfers for friendnumber */ | ||||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum) | void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |         if (ft->file_type == TOX_FILE_KIND_AVATAR) { | ||||||
|         close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); |             close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|         close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Kills all active file transfers for friendnumber */ | ||||||
|  | void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber) | ||||||
|  | { | ||||||
|  |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|  |         close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void kill_all_file_transfers(Tox *m) | void kill_all_file_transfers(Tox *m) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < Friends.max_idx; ++i) { |  | ||||||
|         kill_all_file_transfers_friend(m, Friends.list[i].num); |         kill_all_file_transfers_friend(m, Friends.list[i].num); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,9 +25,9 @@ | |||||||
|  |  | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
|  |  | ||||||
|  | #include "notify.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "notify.h" |  | ||||||
|  |  | ||||||
| #define KiB 1024 | #define KiB 1024 | ||||||
| #define MiB 1048576       /* 1024^2 */ | #define MiB 1048576       /* 1024^2 */ | ||||||
| @@ -51,18 +51,16 @@ struct FileTransfer { | |||||||
|     ToxWindow *window; |     ToxWindow *window; | ||||||
|     FILE *file; |     FILE *file; | ||||||
|     FILE_TRANSFER_STATE state; |     FILE_TRANSFER_STATE state; | ||||||
|     FILE_TRANSFER_DIRECTION direction; |  | ||||||
|     uint8_t file_type; |     uint8_t file_type; | ||||||
|     char file_name[TOX_MAX_FILENAME_LENGTH + 1]; |     char file_name[TOX_MAX_FILENAME_LENGTH + 1]; | ||||||
|     char file_path[PATH_MAX + 1];    /* Not used by senders */ |     char file_path[PATH_MAX + 1];    /* Not used by senders */ | ||||||
|     double   bps; |     double   bps; | ||||||
|     uint32_t filenum; |     uint32_t filenumber; | ||||||
|     uint32_t friendnum; |     uint32_t friendnumber; | ||||||
|     size_t   index; |     size_t   index; | ||||||
|     uint64_t file_size; |     uint64_t file_size; | ||||||
|     uint64_t position; |     uint64_t position; | ||||||
|     time_t   last_line_progress;   /* The last time we updated the progress bar */ |     time_t   last_line_progress;   /* The last time we updated the progress bar */ | ||||||
|     time_t   last_keep_alive;  /* The last time we sent or received data */ |  | ||||||
|     uint32_t line_id; |     uint32_t line_id; | ||||||
|     uint8_t  file_id[TOX_FILE_ID_LENGTH]; |     uint8_t  file_id[TOX_FILE_ID_LENGTH]; | ||||||
| }; | }; | ||||||
| @@ -75,24 +73,24 @@ void init_progress_bar(char *progline); | |||||||
| void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id); | void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id); | ||||||
|  |  | ||||||
| /* refreshes active file transfer status bars. */ | /* refreshes active file transfer status bars. */ | ||||||
| void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum); | void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber); | ||||||
|  |  | ||||||
| /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. | /* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber. | ||||||
|  * Returns NULL if filenum is invalid. |  * Returns NULL if filenumber is invalid. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum); | struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber); | ||||||
|  |  | ||||||
|  |  | ||||||
| /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index, | ||||||
|         FILE_TRANSFER_DIRECTION direction); |         FILE_TRANSFER_DIRECTION direction); | ||||||
|  |  | ||||||
| /* Initializes an unused file transfer and returns its pointer. | /* Initializes an unused file transfer and returns its pointer. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, | struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, | ||||||
|                                        FILE_TRANSFER_DIRECTION direction, uint8_t type); |                                        FILE_TRANSFER_DIRECTION direction, uint8_t type); | ||||||
|  |  | ||||||
| /* Closes file transfer ft. | /* Closes file transfer ft. | ||||||
| @@ -103,8 +101,11 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui | |||||||
| void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | ||||||
|                          Notification sound_type); |                          Notification sound_type); | ||||||
|  |  | ||||||
| /* Kills all active file transfers for friendnum */ | /* Kills active outgoing avatar file transfers for friendnumber */ | ||||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum); | void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber); | ||||||
|  |  | ||||||
|  | /* Kills all active file transfers for friendnumber */ | ||||||
|  | void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber); | ||||||
|  |  | ||||||
| void kill_all_file_transfers(Tox *m); | void kill_all_file_transfers(Tox *m); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										189
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										189
									
								
								src/friendlist.c
									
									
									
									
									
								
							| @@ -20,26 +20,26 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <time.h> |  | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <time.h> | ||||||
|  |  | ||||||
| #include <tox/tox.h> | #include <tox/tox.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "avatars.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "chat.h" | #include "chat.h" | ||||||
| #include "friendlist.h" | #include "friendlist.h" | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "notify.h" |  | ||||||
| #include "help.h" | #include "help.h" | ||||||
|  | #include "line_info.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "avatars.h" | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| #include "audio_call.h" | #include "audio_call.h" | ||||||
| @@ -116,8 +116,8 @@ static void realloc_blocklist(int n) | |||||||
| void kill_friendlist(ToxWindow *self) | void kill_friendlist(ToxWindow *self) | ||||||
| { | { | ||||||
|     for (size_t i = 0; i < Friends.max_idx; ++i) { |     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||||
|         if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL) { |         if (Friends.list[i].active && Friends.list[i].conference_invite.key != NULL) { | ||||||
|             free(Friends.list[i].group_invite.key); |             free(Friends.list[i].conference_invite.key); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -127,6 +127,20 @@ void kill_friendlist(ToxWindow *self) | |||||||
|     del_window(self); |     del_window(self); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void clear_blocklist_index(size_t idx) | ||||||
|  | { | ||||||
|  |     Blocked.list[idx] = (BlockedFriend) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void clear_friendlist_index(size_t idx) | ||||||
|  | { | ||||||
|  |     Friends.list[idx] = (ToxicFriend) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Saves the blocklist to path. If there are no items in the blocklist the | /* Saves the blocklist to path. If there are no items in the blocklist the | ||||||
|  * empty file will be removed. |  * empty file will be removed. | ||||||
|  * |  * | ||||||
| @@ -160,8 +174,7 @@ static int save_blocklist(char *path) | |||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             BlockedFriend tmp; |             BlockedFriend tmp = {0}; | ||||||
|             memset(&tmp, 0, sizeof(BlockedFriend)); |  | ||||||
|             tmp.namelength = htons(Blocked.list[i].namelength); |             tmp.namelength = htons(Blocked.list[i].namelength); | ||||||
|             memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1);  // Include null byte |             memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1);  // Include null byte | ||||||
|             memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE); |             memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE); | ||||||
| @@ -187,13 +200,21 @@ static int save_blocklist(char *path) | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char temp_path[strlen(path) + strlen(TEMP_BLOCKLIST_EXT) + 1]; |     size_t temp_buf_size = strlen(path) + strlen(TEMP_BLOCKLIST_EXT) + 1; | ||||||
|     snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_BLOCKLIST_EXT); |     char *temp_path = malloc(temp_buf_size); | ||||||
|  |  | ||||||
|  |     if (temp_path == NULL) { | ||||||
|  |         free(data); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(temp_path, temp_buf_size, "%s%s", path, TEMP_BLOCKLIST_EXT); | ||||||
|  |  | ||||||
|     FILE *fp = fopen(temp_path, "wb"); |     FILE *fp = fopen(temp_path, "wb"); | ||||||
|  |  | ||||||
|     if (fp == NULL) { |     if (fp == NULL) { | ||||||
|         free(data); |         free(data); | ||||||
|  |         free(temp_path); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -201,6 +222,7 @@ static int save_blocklist(char *path) | |||||||
|         fprintf(stderr, "Failed to write blocklist data.\n"); |         fprintf(stderr, "Failed to write blocklist data.\n"); | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         free(data); |         free(data); | ||||||
|  |         free(temp_path); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -208,9 +230,12 @@ static int save_blocklist(char *path) | |||||||
|     free(data); |     free(data); | ||||||
|  |  | ||||||
|     if (rename(temp_path, path) != 0) { |     if (rename(temp_path, path) != 0) { | ||||||
|  |         free(temp_path); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     free(temp_path); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -235,15 +260,22 @@ int load_blocklist(char *path) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char data[len]; |     char *data = malloc(len); | ||||||
|  |  | ||||||
|  |     if (data == NULL) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (fread(data, len, 1, fp) != 1) { |     if (fread(data, len, 1, fp) != 1) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|  |         free(data); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (len % sizeof(BlockedFriend) != 0) { |     if (len % sizeof(BlockedFriend) != 0) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|  |         free(data); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -251,12 +283,9 @@ int load_blocklist(char *path) | |||||||
|     Blocked.max_idx = num; |     Blocked.max_idx = num; | ||||||
|     realloc_blocklist(num); |     realloc_blocklist(num); | ||||||
|  |  | ||||||
|     int i; |     for (int i = 0; i < num; ++i) { | ||||||
|  |         BlockedFriend tmp = {0}; | ||||||
|     for (i = 0; i < num; ++i) { |         clear_blocklist_index(i); | ||||||
|         BlockedFriend tmp; |  | ||||||
|         memset(&tmp, 0, sizeof(BlockedFriend)); |  | ||||||
|         memset(&Blocked.list[i], 0, sizeof(BlockedFriend)); |  | ||||||
|  |  | ||||||
|         memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend)); |         memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend)); | ||||||
|         Blocked.list[i].namelength = ntohs(tmp.namelength); |         Blocked.list[i].namelength = ntohs(tmp.namelength); | ||||||
| @@ -279,6 +308,8 @@ int load_blocklist(char *path) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fclose(fp); |     fclose(fp); | ||||||
|  |     free(data); | ||||||
|  |  | ||||||
|     sort_blocklist_index(); |     sort_blocklist_index(); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -366,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); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -388,7 +416,7 @@ static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, | |||||||
|         ++Friends.num_online; |         ++Friends.num_online; | ||||||
|  |  | ||||||
|         if (avatar_send(m, num) == -1) { |         if (avatar_send(m, num) == -1) { | ||||||
|             fprintf(stderr, "avatar_send failed for friend %d\n", num); |             fprintf(stderr, "avatar_send failed for friend %u\n", num); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -422,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(); | ||||||
| @@ -457,7 +487,7 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort) | |||||||
|     UNUSED_VAR(self); |     UNUSED_VAR(self); | ||||||
|  |  | ||||||
|     realloc_friends(Friends.max_idx + 1); |     realloc_friends(Friends.max_idx + 1); | ||||||
|     memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend)); |     clear_friendlist_index(Friends.max_idx); | ||||||
|  |  | ||||||
|     uint32_t i; |     uint32_t i; | ||||||
|  |  | ||||||
| @@ -491,10 +521,11 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort) | |||||||
|  |  | ||||||
|         update_friend_last_online(i, t); |         update_friend_last_online(i, t); | ||||||
|  |  | ||||||
|         char tempname[TOX_MAX_NAME_LENGTH] = {0}; |         char tempname[TOX_MAX_NAME_LENGTH + 1]; | ||||||
|         get_nick_truncate(m, tempname, num); |         int name_len = get_nick_truncate(m, tempname, num); | ||||||
|         snprintf(Friends.list[i].name, sizeof(Friends.list[i].name), "%s", tempname); |         memcpy(Friends.list[i].name, tempname, name_len); | ||||||
|         Friends.list[i].namelength = strlen(Friends.list[i].name); |         Friends.list[i].name[name_len] = 0; | ||||||
|  |         Friends.list[i].namelength = name_len; | ||||||
|  |  | ||||||
|         if (i == Friends.max_idx) { |         if (i == Friends.max_idx) { | ||||||
|             ++Friends.max_idx; |             ++Friends.max_idx; | ||||||
| @@ -516,7 +547,7 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort) | |||||||
| static void friendlist_add_blocked(uint32_t fnum, uint32_t bnum) | static void friendlist_add_blocked(uint32_t fnum, uint32_t bnum) | ||||||
| { | { | ||||||
|     realloc_friends(Friends.max_idx + 1); |     realloc_friends(Friends.max_idx + 1); | ||||||
|     memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend)); |     clear_friendlist_index(Friends.max_idx); | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -577,18 +608,19 @@ 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); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key, | static void friendlist_onConferenceInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, | ||||||
|                                      uint16_t length) |         const char *conference_pub_key, | ||||||
|  |         uint16_t length) | ||||||
| { | { | ||||||
|     UNUSED_VAR(self); |     UNUSED_VAR(self); | ||||||
|     UNUSED_VAR(type); |     UNUSED_VAR(type); | ||||||
|     UNUSED_VAR(group_pub_key); |     UNUSED_VAR(conference_pub_key); | ||||||
|     UNUSED_VAR(length); |     UNUSED_VAR(length); | ||||||
|  |  | ||||||
|     if (num >= Friends.max_idx) { |     if (num >= Friends.max_idx) { | ||||||
| @@ -607,8 +639,8 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8 | |||||||
|     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, | ||||||
|                   "* Group 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); | ||||||
| } | } | ||||||
| @@ -654,11 +686,11 @@ static void delete_friend(Tox *m, uint32_t f_num) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (Friends.list[f_num].group_invite.key != NULL) { |     if (Friends.list[f_num].conference_invite.key != NULL) { | ||||||
|         free(Friends.list[f_num].group_invite.key); |         free(Friends.list[f_num].conference_invite.key); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(&Friends.list[f_num], 0, sizeof(ToxicFriend)); |     clear_friendlist_index(f_num); | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -696,7 +728,7 @@ static void delete_blocked_friend(uint32_t bnum); | |||||||
| /* deactivates delete friend popup and deletes friend if instructed */ | /* deactivates delete friend popup and deletes friend if instructed */ | ||||||
| static void del_friend_deactivate(Tox *m, wint_t key) | static void del_friend_deactivate(Tox *m, wint_t key) | ||||||
| { | { | ||||||
|     if (key == 'y') { |     if (key == L'y') { | ||||||
|         if (blocklist_view == 0) { |         if (blocklist_view == 0) { | ||||||
|             delete_friend(m, PendingDelete.num); |             delete_friend(m, PendingDelete.num); | ||||||
|             sort_friendlist_index(); |             sort_friendlist_index(); | ||||||
| @@ -707,7 +739,11 @@ static void del_friend_deactivate(Tox *m, wint_t key) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     delwin(PendingDelete.popup); |     delwin(PendingDelete.popup); | ||||||
|     memset(&PendingDelete, 0, sizeof(PendingDelete)); |  | ||||||
|  |     PendingDelete = (struct PendingDel) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     clear(); |     clear(); | ||||||
|     refresh(); |     refresh(); | ||||||
| } | } | ||||||
| @@ -745,7 +781,7 @@ static void draw_del_popup(void) | |||||||
| /* deletes contact from blocked list */ | /* deletes contact from blocked list */ | ||||||
| static void delete_blocked_friend(uint32_t bnum) | static void delete_blocked_friend(uint32_t bnum) | ||||||
| { | { | ||||||
|     memset(&Blocked.list[bnum], 0, sizeof(BlockedFriend)); |     clear_blocklist_index(bnum); | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -768,12 +804,12 @@ 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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     realloc_blocklist(Blocked.max_idx + 1); |     realloc_blocklist(Blocked.max_idx + 1); | ||||||
|     memset(&Blocked.list[Blocked.max_idx], 0, sizeof(BlockedFriend)); |     clear_blocklist_index(Blocked.max_idx); | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -815,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -836,7 +872,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (key == 'h') { |     if (key == L'h') { | ||||||
|         help_init_menu(self); |         help_init_menu(self); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -859,7 +895,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|  |  | ||||||
|     /* lock screen and force decision on deletion popup */ |     /* lock screen and force decision on deletion popup */ | ||||||
|     if (PendingDelete.active) { |     if (PendingDelete.active) { | ||||||
|         if (key == 'y' || key == 'n') { |         if (key == L'y' || key == L'n') { | ||||||
|             del_friend_deactivate(m, key); |             del_friend_deactivate(m, key); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -871,7 +907,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     switch (key) { |     switch (key) { | ||||||
|         case '\r': |         case L'\r': | ||||||
|             if (blocklist_view) { |             if (blocklist_view) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| @@ -884,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); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -894,7 +930,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|             del_friend_activate(f); |             del_friend_activate(f); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'b': |         case L'b': | ||||||
|             if (!blocklist_view) { |             if (!blocklist_view) { | ||||||
|                 block_friend(m, f); |                 block_friend(m, f); | ||||||
|             } else { |             } else { | ||||||
| @@ -983,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; | ||||||
| @@ -1018,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; | ||||||
| @@ -1030,7 +1068,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|     wprintw(self->window, " Online: "); |     wprintw(self->window, " Online: "); | ||||||
|     wattroff(self->window, A_BOLD); |     wattroff(self->window, A_BOLD); | ||||||
|  |  | ||||||
|     wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends); |     wprintw(self->window, "%zu/%zu \n\n", Friends.num_online, Friends.num_friends); | ||||||
|  |  | ||||||
|     if ((y2 - FLIST_OFST) <= 0) { |     if ((y2 - FLIST_OFST) <= 0) { | ||||||
|         return; |         return; | ||||||
| @@ -1059,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); | ||||||
|  |  | ||||||
|         bool f_selected = false; |  | ||||||
|  |  | ||||||
|         if (is_active) { |         if (is_active) { | ||||||
|  |             bool f_selected = false; | ||||||
|  |  | ||||||
|             if (i == num_selected) { |             if (i == num_selected) { | ||||||
|                 wattron(self->window, A_BOLD); |                 wattron(self->window, A_BOLD); | ||||||
|                 wprintw(self->window, " > "); |                 wprintw(self->window, " > "); | ||||||
| @@ -1208,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; | ||||||
| @@ -1226,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; | ||||||
| @@ -1251,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); | ||||||
|         } |         } | ||||||
| @@ -1302,8 +1355,9 @@ ToxWindow *new_friendlist(void) | |||||||
|         exit_toxic_err("failed in new_friendlist", FATALERR_MEMORY); |         exit_toxic_err("failed in new_friendlist", FATALERR_MEMORY); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret->is_friendlist = true; |     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; | ||||||
| @@ -1313,7 +1367,7 @@ ToxWindow *new_friendlist(void) | |||||||
|     ret->onStatusChange = &friendlist_onStatusChange; |     ret->onStatusChange = &friendlist_onStatusChange; | ||||||
|     ret->onStatusMessageChange = &friendlist_onStatusMessageChange; |     ret->onStatusMessageChange = &friendlist_onStatusMessageChange; | ||||||
|     ret->onFileRecv = &friendlist_onFileRecv; |     ret->onFileRecv = &friendlist_onFileRecv; | ||||||
|     ret->onGroupInvite = &friendlist_onGroupInvite; |     ret->onConferenceInvite = &friendlist_onConferenceInvite; | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     ret->onInvite = &friendlist_onAV; |     ret->onInvite = &friendlist_onAV; | ||||||
| @@ -1327,7 +1381,6 @@ ToxWindow *new_friendlist(void) | |||||||
|     ret->onEnd = &friendlist_onAV; |     ret->onEnd = &friendlist_onAV; | ||||||
|  |  | ||||||
|     ret->is_call = false; |     ret->is_call = false; | ||||||
|     ret->device_selection[0] = ret->device_selection[1] = -1; |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|     ret->num = -1; |     ret->num = -1; | ||||||
| @@ -1340,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; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,9 +25,9 @@ | |||||||
|  |  | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  |  | ||||||
|  | #include "file_transfers.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "file_transfers.h" |  | ||||||
|  |  | ||||||
| struct LastOnline { | struct LastOnline { | ||||||
|     uint64_t last_on; |     uint64_t last_on; | ||||||
| @@ -35,7 +35,7 @@ struct LastOnline { | |||||||
|     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ |     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct GroupChatInvite { | struct ConferenceInvite { | ||||||
|     char *key; |     char *key; | ||||||
|     uint16_t length; |     uint16_t length; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
| @@ -57,7 +57,7 @@ typedef struct { | |||||||
|     Tox_User_Status status; |     Tox_User_Status status; | ||||||
|  |  | ||||||
|     struct LastOnline last_online; |     struct LastOnline last_online; | ||||||
|     struct GroupChatInvite group_invite; |     struct ConferenceInvite conference_invite; | ||||||
|  |  | ||||||
|     struct FileTransfer file_receiver[MAX_FILES]; |     struct FileTransfer file_receiver[MAX_FILES]; | ||||||
|     struct FileTransfer file_sender[MAX_FILES]; |     struct FileTransfer file_sender[MAX_FILES]; | ||||||
| @@ -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); | ||||||
|   | |||||||
| @@ -23,24 +23,25 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "friendlist.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "groupchat.h" |  | ||||||
| #include "prompt.h" |  | ||||||
| #include "help.h" |  | ||||||
| #include "term_mplex.h" |  | ||||||
| #include "avatars.h" | #include "avatars.h" | ||||||
|  | #include "conference.h" | ||||||
|  | #include "friendlist.h" | ||||||
|  | #include "help.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "misc_tools.h" | ||||||
| #include "name_lookup.h" | #include "name_lookup.h" | ||||||
|  | #include "prompt.h" | ||||||
| #include "qr_code.h" | #include "qr_code.h" | ||||||
|  | #include "term_mplex.h" | ||||||
|  | #include "toxic.h" | ||||||
| #include "toxic_strings.h" | #include "toxic_strings.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern char *DATA_FILE; | extern char *DATA_FILE; | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
|  | extern FriendRequests FrndRequests; | ||||||
|  |  | ||||||
| /* command functions */ | /* command functions */ | ||||||
| void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -48,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -68,14 +69,16 @@ 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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); |     FrndRequests.request[req] = (struct friend_request) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -87,7 +90,6 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|  |  | ||||||
|     FrndRequests.max_idx = i; |     FrndRequests.max_idx = i; | ||||||
|     --FrndRequests.num_requests; |     --FrndRequests.num_requests; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | ||||||
| @@ -139,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]) | ||||||
| @@ -147,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -156,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; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -190,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; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -198,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; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -214,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -223,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -232,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]) | ||||||
| @@ -256,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -267,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -284,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: | ||||||
| @@ -306,23 +308,25 @@ 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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); |     FrndRequests.request[req] = (struct friend_request) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -336,17 +340,17 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|     --FrndRequests.num_requests; |     --FrndRequests.num_requests; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     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 group type: text | audio"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -357,31 +361,53 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg | |||||||
|     } 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 group types are: text | audio"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (type != TOX_CONFERENCE_TYPE_TEXT) { |     uint32_t conferencenum = 0; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups."); |  | ||||||
|  |     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||||
|  |         Tox_Err_Conference_New err; | ||||||
|  |  | ||||||
|  |         conferencenum = tox_conference_new(m, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_CONFERENCE_NEW_OK) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } else if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
|  | #ifdef AUDIO | ||||||
|  |         conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL); | ||||||
|  |  | ||||||
|  |         if (conferencenum == (uint32_t) -1) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | #else | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||||
|  |         return; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||||
|  |         tox_conference_delete(m, conferencenum, NULL); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Tox_Err_Conference_New err; | #ifdef AUDIO | ||||||
|  |  | ||||||
|     uint32_t groupnum = tox_conference_new(m, &err); |     if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
|  |         if (!init_conference_audio_input(m, conferencenum)) { | ||||||
|     if (err != TOX_ERR_CONFERENCE_NEW_OK) { |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err); |         } | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) { | #endif | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); |  | ||||||
|         tox_conference_delete(m, groupnum, NULL); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum); |     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]) | ||||||
| @@ -398,44 +424,30 @@ 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->is_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->is_prompt) { |  | ||||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT); |  | ||||||
|         } else if (self->is_groupchat) { |  | ||||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         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->is_chat) { |         if (self->type == WINDOW_TYPE_CHAT) { | ||||||
|             Friends.list[self->num].logging_on = false; |             Friends.list[self->num].logging_on = false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         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]) | ||||||
| @@ -449,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 | ||||||
| @@ -466,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -476,45 +488,79 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     nick[nick_len] = '\0'; |     nick[nick_len] = '\0'; | ||||||
|  |  | ||||||
|     size_t data_file_len = strlen(DATA_FILE); |     size_t data_file_len = strlen(DATA_FILE); | ||||||
|     char dir[data_file_len + 1]; |     char *dir = malloc(data_file_len + 1); | ||||||
|  |  | ||||||
|  |     if (dir == NULL) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); |     size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); | ||||||
|  |  | ||||||
| #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); | ||||||
|         return; |         return; | ||||||
|     } else if (!strcmp(argv[1], "txt")) { |     } else if (!strcmp(argv[1], "txt")) { | ||||||
|  |  | ||||||
| #endif /* QRPNG */ | #endif /* QRPNG */ | ||||||
|         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1]; |         size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT); | ||||||
|         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); |         char *qr_path = malloc(qr_path_buf_size); | ||||||
|  |  | ||||||
|         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { |         if (qr_path == NULL) { | ||||||
|             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: Out of memory"); | ||||||
|  |             free(dir); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); |         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) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|  |             free(dir); | ||||||
|  |             free(qr_path); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         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); | ||||||
|  |  | ||||||
| #ifdef QRPNG | #ifdef QRPNG | ||||||
|     } else if (!strcmp(argv[1], "png")) { |     } else if (!strcmp(argv[1], "png")) { | ||||||
|         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1]; |         size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT_PNG); | ||||||
|         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); |         char *qr_path = malloc(qr_path_buf_size); | ||||||
|  |  | ||||||
|         if (ID_to_QRcode_png(id_string, qr_path) == -1) { |         if (qr_path == NULL) { | ||||||
|             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: Out of memory"); | ||||||
|  |             free(dir); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); |         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) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|  |             free(dir); | ||||||
|  |             free(qr_path); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         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); | ||||||
|  |  | ||||||
|     } 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); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif /* QRPNG */ | #endif /* QRPNG */ | ||||||
|  |  | ||||||
|  |     free(dir); | ||||||
| } | } | ||||||
| #endif /* QRCODE */ | #endif /* QRCODE */ | ||||||
|  |  | ||||||
| @@ -523,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -532,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -550,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -565,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; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -573,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); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -610,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -630,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, ""); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -649,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -664,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: | ||||||
|   | |||||||
| @@ -23,8 +23,8 @@ | |||||||
| #ifndef GLOBAL_COMMANDS_H | #ifndef GLOBAL_COMMANDS_H | ||||||
| #define GLOBAL_COMMANDS_H | #define GLOBAL_COMMANDS_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| @@ -32,7 +32,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
| void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_conference(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| #ifdef QRCODE | #ifdef QRCODE | ||||||
|   | |||||||
| @@ -1,81 +0,0 @@ | |||||||
| /*  group_commands.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 <string.h> |  | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "log.h" |  | ||||||
|  |  | ||||||
| void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) |  | ||||||
| { |  | ||||||
|     UNUSED_VAR(window); |  | ||||||
|  |  | ||||||
|     Tox_Err_Conference_Title err; |  | ||||||
|     char title[MAX_STR_SIZE]; |  | ||||||
|  |  | ||||||
|     if (argc < 1) { |  | ||||||
|         size_t tlen = tox_conference_get_title_size(m, self->num, &err); |  | ||||||
|  |  | ||||||
|         if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) { |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) { |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         title[tlen] = '\0'; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); |  | ||||||
|  |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     snprintf(title, sizeof(title), "%s", argv[1]); |  | ||||||
|     int len = strlen(title); |  | ||||||
|  |  | ||||||
|     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); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     set_window_title(self, title, len); |  | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     char selfnick[TOX_MAX_NAME_LENGTH]; |  | ||||||
|  |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     tox_self_get_name(m, (uint8_t *) selfnick); |  | ||||||
|     size_t sn_len = tox_self_get_name_size(m); |  | ||||||
|     selfnick[sn_len] = '\0'; |  | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title); |  | ||||||
|  |  | ||||||
|     char tmp_event[MAX_STR_SIZE]; |  | ||||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); |  | ||||||
|     write_to_log(tmp_event, selfnick, self->chatwin->log, true); |  | ||||||
| } |  | ||||||
							
								
								
									
										770
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										770
									
								
								src/groupchat.c
									
									
									
									
									
								
							| @@ -1,770 +0,0 @@ | |||||||
| /*  groupchat.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/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _GNU_SOURCE |  | ||||||
| #define _GNU_SOURCE    /* needed for strcasestr() and wcswidth() */ |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <assert.h> |  | ||||||
| #include <time.h> |  | ||||||
| #include <wchar.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <inttypes.h> |  | ||||||
|  |  | ||||||
| #ifdef AUDIO |  | ||||||
| #ifdef __APPLE__ |  | ||||||
| #include <OpenAL/al.h> |  | ||||||
| #include <OpenAL/alc.h> |  | ||||||
| #else |  | ||||||
| #include <AL/al.h> |  | ||||||
| #include <AL/alc.h> |  | ||||||
| /* compatibility with older versions of OpenAL */ |  | ||||||
| #ifndef ALC_ALL_DEVICES_SPECIFIER |  | ||||||
| #include <AL/alext.h> |  | ||||||
| #endif /* ALC_ALL_DEVICES_SPECIFIER */ |  | ||||||
| #endif /* __APPLE__ */ |  | ||||||
| #endif /* AUDIO */ |  | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "groupchat.h" |  | ||||||
| #include "prompt.h" |  | ||||||
| #include "toxic_strings.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "input.h" |  | ||||||
| #include "help.h" |  | ||||||
| #include "notify.h" |  | ||||||
| #include "autocomplete.h" |  | ||||||
| #include "audio_device.h" |  | ||||||
|  |  | ||||||
| extern char *DATA_FILE; |  | ||||||
|  |  | ||||||
| static GroupChat groupchats[MAX_GROUPCHAT_NUM]; |  | ||||||
| static int max_groupchat_index = 0; |  | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; |  | ||||||
| extern struct Winthread Winthread; |  | ||||||
|  |  | ||||||
| #ifdef PYTHON |  | ||||||
| #define AC_NUM_GROUP_COMMANDS_PYTHON 1 |  | ||||||
| #else |  | ||||||
| #define AC_NUM_GROUP_COMMANDS_PYTHON 0 |  | ||||||
| #endif /* PYTHON */ |  | ||||||
| #ifdef QRCODE |  | ||||||
| #define AC_NUM_GROUP_COMMANDS_QRCODE 1 |  | ||||||
| #else |  | ||||||
| #define AC_NUM_GROUP_COMMANDS_QRCODE 0 |  | ||||||
| #endif /* QRCODE */ |  | ||||||
| #define AC_NUM_GROUP_COMMANDS (19 + AC_NUM_GROUP_COMMANDS_PYTHON + AC_NUM_GROUP_COMMANDS_QRCODE) |  | ||||||
|  |  | ||||||
| /* Array of groupchat command names used for tab completion. */ |  | ||||||
| static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = { |  | ||||||
|     { "/accept"     }, |  | ||||||
|     { "/add"        }, |  | ||||||
|     { "/avatar"     }, |  | ||||||
|     { "/clear"      }, |  | ||||||
|     { "/close"      }, |  | ||||||
|     { "/connect"    }, |  | ||||||
|     { "/decline"    }, |  | ||||||
|     { "/exit"       }, |  | ||||||
|     { "/group"      }, |  | ||||||
|     { "/help"       }, |  | ||||||
|     { "/log"        }, |  | ||||||
|     { "/myid"       }, |  | ||||||
| #ifdef QRCODE |  | ||||||
|     { "/myqr"       }, |  | ||||||
| #endif /* QRCODE */ |  | ||||||
|     { "/nick"       }, |  | ||||||
|     { "/note"       }, |  | ||||||
|     { "/nospam"     }, |  | ||||||
|     { "/quit"       }, |  | ||||||
|     { "/requests"   }, |  | ||||||
|     { "/status"     }, |  | ||||||
|     { "/title"      }, |  | ||||||
|  |  | ||||||
| #ifdef PYTHON |  | ||||||
|  |  | ||||||
|     { "/run"        }, |  | ||||||
|  |  | ||||||
| #endif /* PYTHON */ |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static ToxWindow *new_group_chat(uint32_t groupnum); |  | ||||||
|  |  | ||||||
| static void kill_groupchat_window(ToxWindow *self) |  | ||||||
| { |  | ||||||
|     ChatContext *ctx = self->chatwin; |  | ||||||
|  |  | ||||||
|     log_disable(ctx->log); |  | ||||||
|     line_info_cleanup(ctx->hst); |  | ||||||
|     delwin(ctx->linewin); |  | ||||||
|     delwin(ctx->history); |  | ||||||
|     delwin(ctx->sidebar); |  | ||||||
|     free(ctx->log); |  | ||||||
|     free(ctx); |  | ||||||
|     free(self->help); |  | ||||||
|     del_window(self); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int init_groupchat_win(Tox *m, uint32_t groupnum, uint8_t type, const char *title, |  | ||||||
|                        size_t title_length) |  | ||||||
| { |  | ||||||
|     if (groupnum > MAX_GROUPCHAT_NUM) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ToxWindow *self = new_group_chat(groupnum); |  | ||||||
|  |  | ||||||
|     for (int i = 0; i <= max_groupchat_index; ++i) { |  | ||||||
|         if (!groupchats[i].active) { |  | ||||||
|             groupchats[i].chatwin = add_window(m, self); |  | ||||||
|             groupchats[i].active = true; |  | ||||||
|             groupchats[i].num_peers = 0; |  | ||||||
|             groupchats[i].type = type; |  | ||||||
|             groupchats[i].start_time = get_unix_time(); |  | ||||||
|  |  | ||||||
|             set_active_window_index(groupchats[i].chatwin); |  | ||||||
|             set_window_title(self, title, title_length); |  | ||||||
|  |  | ||||||
|             if (i == max_groupchat_index) { |  | ||||||
|                 ++max_groupchat_index; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     kill_groupchat_window(self); |  | ||||||
|  |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void free_groupchat(ToxWindow *self, uint32_t groupnum) |  | ||||||
| { |  | ||||||
|     free(groupchats[groupnum].name_list); |  | ||||||
|     free(groupchats[groupnum].peer_list); |  | ||||||
|     memset(&groupchats[groupnum], 0, sizeof(GroupChat)); |  | ||||||
|  |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = max_groupchat_index; i > 0; --i) { |  | ||||||
|         if (groupchats[i - 1].active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     max_groupchat_index = i; |  | ||||||
|     kill_groupchat_window(self); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void delete_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum) |  | ||||||
| { |  | ||||||
|     tox_conference_delete(m, groupnum, NULL); |  | ||||||
|     free_groupchat(self, groupnum); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* destroys and re-creates groupchat window with or without the peerlist */ |  | ||||||
| void redraw_groupchat_win(ToxWindow *self) |  | ||||||
| { |  | ||||||
|     ChatContext *ctx = self->chatwin; |  | ||||||
|  |  | ||||||
|     endwin(); |  | ||||||
|     refresh(); |  | ||||||
|     clear(); |  | ||||||
|  |  | ||||||
|     int x2, y2; |  | ||||||
|     getmaxyx(stdscr, y2, x2); |  | ||||||
|     y2 -= 2; |  | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (ctx->sidebar) { |  | ||||||
|         delwin(ctx->sidebar); |  | ||||||
|         ctx->sidebar = NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     delwin(ctx->linewin); |  | ||||||
|     delwin(ctx->history); |  | ||||||
|     delwin(self->window); |  | ||||||
|  |  | ||||||
|     self->window = newwin(y2, x2, 0, 0); |  | ||||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); |  | ||||||
|  |  | ||||||
|     if (self->show_peerlist) { |  | ||||||
|         ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); |  | ||||||
|         ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); |  | ||||||
|     } else { |  | ||||||
|         ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     scrollok(ctx->history, 0); |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum, |  | ||||||
|                                      Tox_Message_Type type, const char *msg, size_t len) |  | ||||||
| { |  | ||||||
|     UNUSED_VAR(len); |  | ||||||
|  |  | ||||||
|     if (self->num != groupnum) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |  | ||||||
|  |  | ||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |  | ||||||
|     get_group_nick_truncate(m, nick, peernum, groupnum); |  | ||||||
|  |  | ||||||
|     char selfnick[TOX_MAX_NAME_LENGTH]; |  | ||||||
|     tox_self_get_name(m, (uint8_t *) selfnick); |  | ||||||
|  |  | ||||||
|     size_t sn_len = tox_self_get_name_size(m); |  | ||||||
|     selfnick[sn_len] = '\0'; |  | ||||||
|  |  | ||||||
|     int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN; |  | ||||||
|  |  | ||||||
|     /* Only play sound if mentioned by someone else */ |  | ||||||
|     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) { |  | ||||||
|             box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg); |  | ||||||
|         } else { |  | ||||||
|             box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         nick_clr = RED; |  | ||||||
|     } else { |  | ||||||
|         sound_notify(self, silent, NT_WNDALERT_1, NULL); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     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); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum, |  | ||||||
|         const char *title, |  | ||||||
|         size_t length) |  | ||||||
| { |  | ||||||
|     ChatContext *ctx = self->chatwin; |  | ||||||
|  |  | ||||||
|     if (self->num != groupnum) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     set_window_title(self, title, length); |  | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     /* don't announce title when we join the room */ |  | ||||||
|     if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT)) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |  | ||||||
|     get_group_nick_truncate(m, nick, peernum, groupnum); |  | ||||||
|     line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title); |  | ||||||
|  |  | ||||||
|     char tmp_event[MAX_STR_SIZE]; |  | ||||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); |  | ||||||
|     write_to_log(tmp_event, nick, ctx->log, true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void group_update_name_list(uint32_t groupnum) |  | ||||||
| { |  | ||||||
|     GroupChat *chat = &groupchats[groupnum]; |  | ||||||
|  |  | ||||||
|     if (!chat->active) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (chat->name_list) { |  | ||||||
|         free(chat->name_list); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     chat->name_list = malloc(sizeof(char *) * chat->num_peers * TOX_MAX_NAME_LENGTH); |  | ||||||
|  |  | ||||||
|     if (chat->name_list == NULL) { |  | ||||||
|         exit_toxic_err("failed in group_update_name_list", FATALERR_MEMORY); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint32_t i, count = 0; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < chat->max_idx; ++i) { |  | ||||||
|         if (chat->peer_list[i].active) { |  | ||||||
|             memcpy(&chat->name_list[count * TOX_MAX_NAME_LENGTH], chat->peer_list[i].name, chat->peer_list[i].name_length + 1); |  | ||||||
|             ++count; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qsort(chat->name_list, count, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Reallocates groupnum's peer list. Increase is true if the list needs to grow. |  | ||||||
|  * |  | ||||||
|  * Returns 0 on success. |  | ||||||
|  * Returns -1 on failure. |  | ||||||
|  */ |  | ||||||
| static int realloc_peer_list(GroupChat *chat, uint32_t num_peers) |  | ||||||
| { |  | ||||||
|     if (!chat) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (num_peers == 0) { |  | ||||||
|         free(chat->peer_list); |  | ||||||
|         chat->peer_list = NULL; |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct GroupPeer *tmp_list = realloc(chat->peer_list, num_peers * sizeof(struct GroupPeer)); |  | ||||||
|  |  | ||||||
|     if (!tmp_list) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     chat->peer_list = tmp_list; |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void update_peer_list(Tox *m, uint32_t groupnum, uint32_t num_peers) |  | ||||||
| { |  | ||||||
|     GroupChat *chat = &groupchats[groupnum]; |  | ||||||
|  |  | ||||||
|     if (!chat->active) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     realloc_peer_list(chat, num_peers); |  | ||||||
|  |  | ||||||
|     uint32_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < num_peers; ++i) { |  | ||||||
|         GroupPeer *peer = &chat->peer_list[i]; |  | ||||||
|  |  | ||||||
|         Tox_Err_Conference_Peer_Query err; |  | ||||||
|         size_t length = tox_conference_peer_get_name_size(m, groupnum, i, &err); |  | ||||||
|  |  | ||||||
|         if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK || length >= TOX_MAX_NAME_LENGTH) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         tox_conference_peer_get_name(m, groupnum, i, (uint8_t *) peer->name, &err); |  | ||||||
|         peer->name[length] = 0; |  | ||||||
|  |  | ||||||
|         if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         peer->active = true; |  | ||||||
|         peer->name_length = length; |  | ||||||
|         peer->peernumber = i; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     group_update_name_list(groupnum); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void groupchat_onGroupNameListChange(ToxWindow *self, Tox *m, uint32_t groupnum) |  | ||||||
| { |  | ||||||
|     if (self->num != groupnum) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (groupnum > max_groupchat_index) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     GroupChat *chat = &groupchats[groupnum]; |  | ||||||
|  |  | ||||||
|     if (!chat->active) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Tox_Err_Conference_Peer_Query err; |  | ||||||
|  |  | ||||||
|     uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err); |  | ||||||
|     uint32_t old_num = chat->num_peers; |  | ||||||
|  |  | ||||||
|     if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) { |  | ||||||
|         chat->num_peers = num_peers; |  | ||||||
|     } else { |  | ||||||
|         num_peers = old_num; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     chat->max_idx = num_peers; |  | ||||||
|     update_peer_list(m, groupnum, num_peers); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void groupchat_onGroupPeerNameChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum, |  | ||||||
|         const char *name, size_t length) |  | ||||||
| { |  | ||||||
|     UNUSED_VAR(length); |  | ||||||
|  |  | ||||||
|     if (self->num != groupnum) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     GroupChat *chat = &groupchats[groupnum]; |  | ||||||
|  |  | ||||||
|     if (!chat->active) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint32_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < chat->max_idx; ++i) { |  | ||||||
|         GroupPeer *peer = &chat->peer_list[i]; |  | ||||||
|  |  | ||||||
|         // Test against default tox name to prevent nick change spam on initial join (TODO: this is disgusting) |  | ||||||
|         if (peer->active && peer->peernumber == peernum && peer->name_length > 0) { |  | ||||||
|             ChatContext *ctx = self->chatwin; |  | ||||||
|             char timefrmt[TIME_STR_SIZE]; |  | ||||||
|             get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|             char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; |  | ||||||
|             snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name); |  | ||||||
|  |  | ||||||
|             write_to_log(tmp_event, peer->name, ctx->log, true); |  | ||||||
|             line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as "); |  | ||||||
|  |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     groupchat_onGroupNameListChange(self, m, groupnum); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action) |  | ||||||
| { |  | ||||||
|     if (action == NULL) { |  | ||||||
|         wprintw(ctx->history, "Invalid syntax.\n"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Tox_Err_Conference_Send_Message 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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Return true if input is recognized by handler |  | ||||||
|  */ |  | ||||||
| static bool groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) |  | ||||||
| { |  | ||||||
|     ChatContext *ctx = self->chatwin; |  | ||||||
|  |  | ||||||
|     int x, y, y2, x2; |  | ||||||
|     getyx(self->window, y, x); |  | ||||||
|     getmaxyx(self->window, y2, x2); |  | ||||||
|  |  | ||||||
|     UNUSED_VAR(y); |  | ||||||
|  |  | ||||||
|     if (x2 <= 0 || y2 <= 0) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (self->help->active) { |  | ||||||
|         help_onKey(self, key); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (ctx->pastemode && key == '\r') { |  | ||||||
|         key = '\n'; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (ltr || key == '\n') {    /* char is printable */ |  | ||||||
|         input_new_char(self, key, x, x2); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (line_info_onKey(self, key)) { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (input_handle(self, key, x, x2)) { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool input_ret = false; |  | ||||||
|  |  | ||||||
|     if (key == '\t') {  /* TAB key: auto-completes peer name or command */ |  | ||||||
|         input_ret = true; |  | ||||||
|  |  | ||||||
|         if (ctx->len > 0) { |  | ||||||
|             int diff; |  | ||||||
|  |  | ||||||
|             /* TODO: make this not suck */ |  | ||||||
|             if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) { |  | ||||||
|                 diff = complete_line(self, groupchats[self->num].name_list, groupchats[self->num].num_peers, |  | ||||||
|                                      TOX_MAX_NAME_LENGTH); |  | ||||||
|             } else if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) { |  | ||||||
|                 diff = dir_match(self, m, ctx->line, L"/avatar"); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
| #ifdef PYTHON |  | ||||||
|             else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) { |  | ||||||
|                 diff = dir_match(self, m, ctx->line, L"/run"); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
|             else { |  | ||||||
|                 diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (diff != -1) { |  | ||||||
|                 if (x + diff > x2 - 1) { |  | ||||||
|                     int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); |  | ||||||
|                     ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 sound_notify(self, notif_error, 0, NULL); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             sound_notify(self, notif_error, 0, NULL); |  | ||||||
|         } |  | ||||||
|     } else if (key == T_KEY_C_DOWN) {    /* Scroll peerlist up and down one position */ |  | ||||||
|         input_ret = true; |  | ||||||
|         const int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST; |  | ||||||
|  |  | ||||||
|         if (groupchats[self->num].side_pos < (int64_t) groupchats[self->num].num_peers - L) { |  | ||||||
|             ++groupchats[self->num].side_pos; |  | ||||||
|         } |  | ||||||
|     } else if (key == T_KEY_C_UP) { |  | ||||||
|         input_ret = true; |  | ||||||
|  |  | ||||||
|         if (groupchats[self->num].side_pos > 0) { |  | ||||||
|             --groupchats[self->num].side_pos; |  | ||||||
|         } |  | ||||||
|     } else if (key == '\r') { |  | ||||||
|         input_ret = true; |  | ||||||
|         rm_trailing_spaces_buf(ctx); |  | ||||||
|  |  | ||||||
|         if (!wstring_is_empty(ctx->line)) { |  | ||||||
|             add_line_to_hist(ctx); |  | ||||||
|  |  | ||||||
|             wstrsubst(ctx->line, L'¶', L'\n'); |  | ||||||
|  |  | ||||||
|             char line[MAX_STR_SIZE]; |  | ||||||
|  |  | ||||||
|             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { |  | ||||||
|                 memset(&line, 0, sizeof(line)); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (line[0] == '/') { |  | ||||||
|                 if (strcmp(line, "/close") == 0) { |  | ||||||
|                     delete_groupchat(self, m, self->num); |  | ||||||
|                     return true; |  | ||||||
|                 } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { |  | ||||||
|                     send_group_action(self, ctx, m, line + strlen("/me ")); |  | ||||||
|                 } else { |  | ||||||
|                     execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 Tox_Err_Conference_Send_Message 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); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         wclear(ctx->linewin); |  | ||||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); |  | ||||||
|         reset_buf(ctx); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return input_ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void groupchat_onDraw(ToxWindow *self, Tox *m) |  | ||||||
| { |  | ||||||
|     UNUSED_VAR(m); |  | ||||||
|  |  | ||||||
|     int x2, y2; |  | ||||||
|     getmaxyx(self->window, y2, x2); |  | ||||||
|  |  | ||||||
|     if (x2 <= 0 || y2 <= 0) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |  | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |  | ||||||
|     line_info_print(self); |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |  | ||||||
|  |  | ||||||
|     wclear(ctx->linewin); |  | ||||||
|  |  | ||||||
|     curs_set(1); |  | ||||||
|  |  | ||||||
|     if (ctx->len > 0) { |  | ||||||
|         mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     wclear(ctx->sidebar); |  | ||||||
|     mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); |  | ||||||
|  |  | ||||||
|     if (self->show_peerlist) { |  | ||||||
|         mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT); |  | ||||||
|         mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); |  | ||||||
|  |  | ||||||
|         pthread_mutex_lock(&Winthread.lock); |  | ||||||
|         uint32_t num_peers = groupchats[self->num].num_peers; |  | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |  | ||||||
|  |  | ||||||
|         wmove(ctx->sidebar, 0, 1); |  | ||||||
|         wattron(ctx->sidebar, A_BOLD); |  | ||||||
|         wprintw(ctx->sidebar, "Peers: %"PRIu32"\n", num_peers); |  | ||||||
|         wattroff(ctx->sidebar, A_BOLD); |  | ||||||
|  |  | ||||||
|         mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE); |  | ||||||
|         mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1); |  | ||||||
|  |  | ||||||
|         int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT; |  | ||||||
|         uint32_t i; |  | ||||||
|  |  | ||||||
|         for (i = 0; i < num_peers && i < maxlines; ++i) { |  | ||||||
|             wmove(ctx->sidebar, i + 2, 1); |  | ||||||
|  |  | ||||||
|             pthread_mutex_lock(&Winthread.lock); |  | ||||||
|             uint32_t peer = i + groupchats[self->num].side_pos; |  | ||||||
|             pthread_mutex_unlock(&Winthread.lock); |  | ||||||
|  |  | ||||||
|             /* truncate nick to fit in side panel without modifying list */ |  | ||||||
|             char tmpnck[TOX_MAX_NAME_LENGTH]; |  | ||||||
|             int maxlen = SIDEBAR_WIDTH - 2; |  | ||||||
|  |  | ||||||
|             pthread_mutex_lock(&Winthread.lock); |  | ||||||
|             memcpy(tmpnck, &groupchats[self->num].name_list[peer * TOX_MAX_NAME_LENGTH], maxlen); |  | ||||||
|             pthread_mutex_unlock(&Winthread.lock); |  | ||||||
|  |  | ||||||
|             tmpnck[maxlen] = '\0'; |  | ||||||
|  |  | ||||||
|             wprintw(ctx->sidebar, "%s\n", tmpnck); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int y, x; |  | ||||||
|     getyx(self->window, y, x); |  | ||||||
|  |  | ||||||
|     UNUSED_VAR(x); |  | ||||||
|  |  | ||||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); |  | ||||||
|     wmove(self->window, y + 1, new_x); |  | ||||||
|  |  | ||||||
|     wnoutrefresh(self->window); |  | ||||||
|  |  | ||||||
|     if (self->help->active) { |  | ||||||
|         help_onDraw(self); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void groupchat_onInit(ToxWindow *self, Tox *m) |  | ||||||
| { |  | ||||||
|     int x2, y2; |  | ||||||
|     getmaxyx(self->window, y2, x2); |  | ||||||
|  |  | ||||||
|     if (x2 <= 0 || y2 <= 0) { |  | ||||||
|         exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |  | ||||||
|  |  | ||||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 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->hst = calloc(1, sizeof(struct history)); |  | ||||||
|     ctx->log = calloc(1, sizeof(struct chatlog)); |  | ||||||
|  |  | ||||||
|     if (ctx->log == NULL || ctx->hst == NULL) { |  | ||||||
|         exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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_GROUP) == -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); |  | ||||||
|     wmove(self->window, y2 - CURS_Y_OFFSET, 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static ToxWindow *new_group_chat(uint32_t groupnum) |  | ||||||
| { |  | ||||||
|     ToxWindow *ret = calloc(1, sizeof(ToxWindow)); |  | ||||||
|  |  | ||||||
|     if (ret == NULL) { |  | ||||||
|         exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret->is_groupchat = true; |  | ||||||
|  |  | ||||||
|     ret->onKey = &groupchat_onKey; |  | ||||||
|     ret->onDraw = &groupchat_onDraw; |  | ||||||
|     ret->onInit = &groupchat_onInit; |  | ||||||
|     ret->onGroupMessage = &groupchat_onGroupMessage; |  | ||||||
|     ret->onGroupNameListChange = &groupchat_onGroupNameListChange; |  | ||||||
|     ret->onGroupPeerNameChange = &groupchat_onGroupPeerNameChange; |  | ||||||
|     ret->onGroupTitleChange = &groupchat_onGroupTitleChange; |  | ||||||
|  |  | ||||||
|     snprintf(ret->name, sizeof(ret->name), "Group %u", groupnum); |  | ||||||
|  |  | ||||||
|     ChatContext *chatwin = calloc(1, sizeof(ChatContext)); |  | ||||||
|     Help *help = calloc(1, sizeof(Help)); |  | ||||||
|  |  | ||||||
|     if (chatwin == NULL || help == NULL) { |  | ||||||
|         exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret->chatwin = chatwin; |  | ||||||
|     ret->help = help; |  | ||||||
|  |  | ||||||
|     ret->num = groupnum; |  | ||||||
|     ret->show_peerlist = true; |  | ||||||
|     ret->active_box = -1; |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| /*  groupchat.h |  | ||||||
|  * |  | ||||||
|  * |  | ||||||
|  *  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/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef GROUPCHAT_H |  | ||||||
| #define GROUPCHAT_H |  | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
|  |  | ||||||
| #define SIDEBAR_WIDTH 16 |  | ||||||
| #define SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */ |  | ||||||
| #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2 |  | ||||||
| #define GROUP_EVENT_WAIT 3 |  | ||||||
|  |  | ||||||
| typedef struct GroupPeer { |  | ||||||
|     bool       active; |  | ||||||
|     char       name[TOX_MAX_NAME_LENGTH]; |  | ||||||
|     size_t     name_length; |  | ||||||
|     uint32_t   peernumber; |  | ||||||
| } GroupPeer; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     int chatwin; |  | ||||||
|     bool active; |  | ||||||
|     uint8_t type; |  | ||||||
|     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ |  | ||||||
|     time_t start_time; |  | ||||||
|  |  | ||||||
|     GroupPeer *peer_list; |  | ||||||
|     uint32_t max_idx; |  | ||||||
|  |  | ||||||
|     char *name_list; |  | ||||||
|     uint32_t num_peers; |  | ||||||
|  |  | ||||||
| } GroupChat; |  | ||||||
|  |  | ||||||
| /* Frees all Toxic associated data structures for a groupchat (does not call tox_conference_delete() ) */ |  | ||||||
| void free_groupchat(ToxWindow *self, uint32_t groupnum); |  | ||||||
|  |  | ||||||
| int init_groupchat_win(Tox *m, uint32_t groupnum, uint8_t type, const char *title, size_t title_length); |  | ||||||
|  |  | ||||||
| /* destroys and re-creates groupchat window with or without the peerlist */ |  | ||||||
| void redraw_groupchat_win(ToxWindow *self); |  | ||||||
|  |  | ||||||
| #endif /* GROUPCHAT_H */ |  | ||||||
							
								
								
									
										75
									
								
								src/help.c
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								src/help.c
									
									
									
									
									
								
							| @@ -22,10 +22,10 @@ | |||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "help.h" | #include "help.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
| #include "api.h" | #include "api.h" | ||||||
| @@ -59,7 +59,10 @@ void help_init_menu(ToxWindow *self) | |||||||
| static void help_exit(ToxWindow *self) | static void help_exit(ToxWindow *self) | ||||||
| { | { | ||||||
|     delwin(self->help->win); |     delwin(self->help->win); | ||||||
|     memset(self->help, 0, sizeof(Help)); |  | ||||||
|  |     *(self->help) = (struct Help) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void help_init_window(ToxWindow *self, int height, int width) | static void help_init_window(ToxWindow *self, int height, int width) | ||||||
| @@ -101,11 +104,11 @@ static void help_draw_menu(ToxWindow *self) | |||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|     wprintw(win, "hat commands\n"); |     wprintw(win, "hat commands\n"); | ||||||
|  |  | ||||||
|     wprintw(win, " g"); |     wprintw(win, " c"); | ||||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|     wprintw(win, "r"); |     wprintw(win, "o"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|     wprintw(win, "oup commands\n"); |     wprintw(win, "nference commands\n"); | ||||||
|  |  | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
| @@ -176,7 +179,7 @@ static void help_draw_global(ToxWindow *self) | |||||||
|     wprintw(win, "  /nick <nick>               : Set your nickname\n"); |     wprintw(win, "  /nick <nick>               : Set your nickname\n"); | ||||||
|     wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); |     wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); | ||||||
|     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); |     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); | ||||||
|     wprintw(win, "  /group <type>              : Create a group chat where type: text | audio\n"); |     wprintw(win, "  /conference <type>         : Create a conference where type: text | audio\n"); | ||||||
|     wprintw(win, "  /myid                      : Print your Tox ID\n"); |     wprintw(win, "  /myid                      : Print your Tox ID\n"); | ||||||
| #ifdef QRCODE | #ifdef QRCODE | ||||||
| #ifdef QRPNG | #ifdef QRPNG | ||||||
| @@ -231,8 +234,8 @@ static void help_draw_chat(ToxWindow *self) | |||||||
|     wprintw(win, "Chat Commands:\n"); |     wprintw(win, "Chat Commands:\n"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|     wprintw(win, "  /invite <n>                : Invite contact to a group chat\n"); |     wprintw(win, "  /invite <n>                : Invite contact to a conference \n"); | ||||||
|     wprintw(win, "  /join                      : Join a pending group chat\n"); |     wprintw(win, "  /join                      : Join a pending conference\n"); | ||||||
|     wprintw(win, "  /sendfile <path>           : Send a file\n"); |     wprintw(win, "  /sendfile <path>           : Send a file\n"); | ||||||
|     wprintw(win, "  /savefile <id>             : Receive a file\n"); |     wprintw(win, "  /savefile <id>             : Receive a file\n"); | ||||||
|     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); |     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); | ||||||
| @@ -256,7 +259,9 @@ static void help_draw_chat(ToxWindow *self) | |||||||
|     wattron(win, A_BOLD); |     wattron(win, A_BOLD); | ||||||
|     wprintw(win, "\n Video:\n"); |     wprintw(win, "\n Video:\n"); | ||||||
|     wattroff(win, A_BOLD); |     wattroff(win, A_BOLD); | ||||||
|     wprintw(win, "  /video                     : Toggle video call\n"); |     wprintw(win, "  /res <width> <height>      : Set video resolution\n"); | ||||||
|  |     wprintw(win, "  /vcall                     : Video call\n"); | ||||||
|  |     wprintw(win, "  /video                     : Toggle video in call\n"); | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
| @@ -279,8 +284,8 @@ static void help_draw_keys(ToxWindow *self) | |||||||
|     wprintw(win, "  Page Up and Page Down     : Scroll window history one line\n"); |     wprintw(win, "  Page Up and Page Down     : Scroll window history one line\n"); | ||||||
|     wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n"); |     wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n"); | ||||||
|     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); |     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); | ||||||
|     wprintw(win, "  Ctrl+up and Ctrl+down     : Scroll peer list in groupchats\n"); |     wprintw(win, "  Ctrl+up and Ctrl+down     : Scroll peer list in conference\n"); | ||||||
|     wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n"); |     wprintw(win, "  Ctrl+B                    : Toggle the conference peerlist\n"); | ||||||
|     wprintw(win, "  Ctrl+J                    : Insert new line\n"); |     wprintw(win, "  Ctrl+J                    : Insert new line\n"); | ||||||
|     wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n"); |     wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n"); | ||||||
|     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); |     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); | ||||||
| @@ -291,17 +296,27 @@ static void help_draw_keys(ToxWindow *self) | |||||||
|     wnoutrefresh(win); |     wnoutrefresh(win); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void help_draw_group(ToxWindow *self) | static void help_draw_conference(ToxWindow *self) | ||||||
| { | { | ||||||
|     WINDOW *win = self->help->win; |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|     wmove(win, 1, 1); |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|     wattron(win, A_BOLD | COLOR_PAIR(RED)); |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|     wprintw(win, "Group 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 group title (show current title if no msg)\n\n"); |     wprintw(win, "  /title <msg>               : Show/set conference title\n"); | ||||||
|  | #ifdef AUDIO | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, "\n Audio:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |     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 <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"); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
| @@ -356,14 +371,14 @@ void help_onKey(ToxWindow *self, wint_t key) | |||||||
|     int height; |     int height; | ||||||
|  |  | ||||||
|     switch (key) { |     switch (key) { | ||||||
|         case 'x': |         case L'x': | ||||||
|         case T_KEY_ESC: |         case T_KEY_ESC: | ||||||
|             help_exit(self); |             help_exit(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'c': |         case L'c': | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|             help_init_window(self, 23, 80); |             help_init_window(self, 25, 80); | ||||||
| #elif AUDIO | #elif AUDIO | ||||||
|             help_init_window(self, 20, 80); |             help_init_window(self, 20, 80); | ||||||
| #else | #else | ||||||
| @@ -372,7 +387,7 @@ void help_onKey(ToxWindow *self, wint_t key) | |||||||
|             self->help->type = HELP_CHAT; |             self->help->type = HELP_CHAT; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'g': |         case L'g': | ||||||
|             height = 22; |             height = 22; | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|             height += 8; |             height += 8; | ||||||
| @@ -386,30 +401,34 @@ void help_onKey(ToxWindow *self, wint_t key) | |||||||
|             self->help->type = HELP_GLOBAL; |             self->help->type = HELP_GLOBAL; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'r': |         case L'o': | ||||||
|             help_init_window(self, 6, 80); |             height = 6; | ||||||
|             self->help->type = HELP_GROUP; | #ifdef AUDIO | ||||||
|  |             height += 7; | ||||||
|  | #endif | ||||||
|  |             help_init_window(self, height, 80); | ||||||
|  |             self->help->type = HELP_CONFERENCE; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
|  |  | ||||||
|         case 'p': |         case L'p': | ||||||
|             help_init_window(self, 4 + num_registered_handlers(), help_max_width()); |             help_init_window(self, 4 + num_registered_handlers(), help_max_width()); | ||||||
|             self->help->type = HELP_PLUGIN; |             self->help->type = HELP_PLUGIN; | ||||||
|             break; |             break; | ||||||
| #endif /* PYTHON */ | #endif /* PYTHON */ | ||||||
|  |  | ||||||
|         case 'f': |         case L'f': | ||||||
|             help_init_window(self, 10, 80); |             help_init_window(self, 10, 80); | ||||||
|             self->help->type = HELP_CONTACTS; |             self->help->type = HELP_CONTACTS; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'k': |         case L'k': | ||||||
|             help_init_window(self, 15, 80); |             help_init_window(self, 15, 80); | ||||||
|             self->help->type = HELP_KEYS; |             self->help->type = HELP_KEYS; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'm': |         case L'm': | ||||||
|             help_init_menu(self); |             help_init_menu(self); | ||||||
|             self->help->type = HELP_MENU; |             self->help->type = HELP_MENU; | ||||||
|             break; |             break; | ||||||
| @@ -439,8 +458,8 @@ void help_onDraw(ToxWindow *self) | |||||||
|             help_draw_contacts(self); |             help_draw_contacts(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case HELP_GROUP: |         case HELP_CONFERENCE: | ||||||
|             help_draw_group(self); |             help_draw_conference(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ typedef enum { | |||||||
|     HELP_MENU, |     HELP_MENU, | ||||||
|     HELP_GLOBAL, |     HELP_GLOBAL, | ||||||
|     HELP_CHAT, |     HELP_CHAT, | ||||||
|     HELP_GROUP, |     HELP_CONFERENCE, | ||||||
|     HELP_KEYS, |     HELP_KEYS, | ||||||
|     HELP_CONTACTS, |     HELP_CONTACTS, | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								src/input.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/input.c
									
									
									
									
									
								
							| @@ -26,14 +26,14 @@ | |||||||
|  |  | ||||||
| #include <wchar.h> | #include <wchar.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "conference.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "toxic_strings.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
| #include "groupchat.h" |  | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| @@ -334,9 +334,9 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x) | |||||||
|        maybe convert entire function to if/else and make them all customizable keys? */ |        maybe convert entire function to if/else and make them all customizable keys? */ | ||||||
|     if (!match) { |     if (!match) { | ||||||
|         if (key == user_settings->key_toggle_peerlist) { |         if (key == user_settings->key_toggle_peerlist) { | ||||||
|             if (self->is_groupchat) { |             if (self->type == WINDOW_TYPE_CONFERENCE) { | ||||||
|                 self->show_peerlist ^= 1; |                 self->show_peerlist ^= 1; | ||||||
|                 redraw_groupchat_win(self); |                 redraw_conference_win(self); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             match = true; |             match = true; | ||||||
|   | |||||||
							
								
								
									
										530
									
								
								src/line_info.c
									
									
									
									
									
								
							
							
						
						
									
										530
									
								
								src/line_info.c
									
									
									
									
									
								
							| @@ -20,19 +20,19 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stdbool.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "conference.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "groupchat.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "notify.h" |  | ||||||
| #include "message_queue.h" | #include "message_queue.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| @@ -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->is_chat || self->is_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 groupchat 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->is_groupchat) { |     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'); | ||||||
|                 while (msg) { |                     break; | ||||||
|                     char *line = strsep(&msg, "\n"); |  | ||||||
|  |  | ||||||
|                     if (line[0] == '>') { |  | ||||||
|                         wattron(win, COLOR_PAIR(GREEN)); |  | ||||||
|                     } else if (line[0] == '<') { |  | ||||||
|                         wattron(win, COLOR_PAIR(RED)); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     wprintw(win, "%s%c", line, msg ? '\n' : '\0'); |  | ||||||
|  |  | ||||||
|                     if (line[0] == '>') { |  | ||||||
|                         wattroff(win, COLOR_PAIR(GREEN)); |  | ||||||
|                     } else if (line[0] == '<') { |  | ||||||
|                         wattroff(win, COLOR_PAIR(RED)); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     // change the \0 set by strsep back to \n |  | ||||||
|                     if (msg) { |  | ||||||
|                         msg[-1] = '\n'; |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { |                 if (line->msg[0] == '>') { | ||||||
|  |                     wattron(win, COLOR_PAIR(GREEN)); | ||||||
|  |                 } else if (line->msg[0] == '<') { | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |                     wattron(win, COLOR_PAIR(RED)); | ||||||
|                     wprintw(win, " x"); |                 } | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |  | ||||||
|  |  | ||||||
|                     if (line->noread_flag == false) { |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |  | ||||||
|  |                 if (line->msg[0] == '>') { | ||||||
|  |                     wattroff(win, COLOR_PAIR(GREEN)); | ||||||
|  |                 } else if (line->msg[0] == '<') { | ||||||
|  |                     wattroff(win, COLOR_PAIR(RED)); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (type == OUT_MSG && !line->read_flag) { | ||||||
|  |                     if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|                         line->noread_flag = true; |                         line->noread_flag = true; | ||||||
|                         line->len += 2; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n", line->msg); |                 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", line->msg); |  | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |  | ||||||
|  |  | ||||||
|                     if (line->noread_flag == false) { |  | ||||||
|                         line->noread_flag = true; |                         line->noread_flag = true; | ||||||
|                         line->len += 2; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n", line->msg); |                 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 { | ||||||
|  |             hst->line_start = next; | ||||||
|  |         } | ||||||
|     } else { |     } else { | ||||||
|         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); |         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 { | ||||||
|   | |||||||
| @@ -23,15 +23,15 @@ | |||||||
| #ifndef LINE_INFO_H | #ifndef LINE_INFO_H | ||||||
| #define LINE_INFO_H | #define LINE_INFO_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| #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 */ | ||||||
|   | |||||||
							
								
								
									
										264
									
								
								src/log.c
									
									
									
									
									
								
							
							
						
						
									
										264
									
								
								src/log.c
									
									
									
									
									
								
							| @@ -22,42 +22,46 @@ | |||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <time.h> |  | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | #include <time.h> | ||||||
|  |  | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "settings.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "line_info.h" |  | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| /* There are three types of logs: chat logs, groupchat 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 groupchat log is in the format: LOGDIR/selfkey-groupname-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_GROUP: |  | ||||||
|             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,92 +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->file != NULL) { |     if (log == NULL) { | ||||||
|         fclose(log->file); |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(log, 0, sizeof(struct chatlog)); |     if (log->file != NULL) { | ||||||
|  |         fclose(log->file); | ||||||
|  |         log->file = NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     log->lastwrite = 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) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (log->file != NULL) { |     if (log->log_on) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { |     if (*log->path == 0) { | ||||||
|         log_disable(log); |  | ||||||
|         return -1; |         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; | ||||||
| @@ -248,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -258,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_on) { |     if (log != NULL) { | ||||||
|         log_enable(dest, selfkey, otherkey, log, LOG_CHAT); |         memcpy(log->path, newpath, new_path_len); | ||||||
|  |         log->path[new_path_len] = 0; | ||||||
|  |  | ||||||
|  |         if (log_on) { | ||||||
|  |             log_enable(log); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -283,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_GROUP, |     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 */ | ||||||
|   | |||||||
| @@ -22,12 +22,12 @@ | |||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "message_queue.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
|  | #include "message_queue.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| void cqueue_cleanup(struct chat_queue *q) | void cqueue_cleanup(struct chat_queue *q) | ||||||
| { | { | ||||||
| @@ -59,7 +59,7 @@ void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, | |||||||
|     new_m->type = type; |     new_m->type = type; | ||||||
|     new_m->line_id = line_id; |     new_m->line_id = line_id; | ||||||
|     new_m->last_send_try = 0; |     new_m->last_send_try = 0; | ||||||
|     new_m->receipt = 0; |     new_m->receipt = -1; | ||||||
|     new_m->next = NULL; |     new_m->next = NULL; | ||||||
|  |  | ||||||
|     if (q->root == NULL) { |     if (q->root == NULL) { | ||||||
| @@ -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; | ||||||
| @@ -98,6 +98,7 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg) | |||||||
| /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | ||||||
| void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | ||||||
| { | { | ||||||
|  |     struct chatlog *log = self->chatwin->log; | ||||||
|     struct chat_queue *q = self->chatwin->cqueue; |     struct chat_queue *q = self->chatwin->cqueue; | ||||||
|     struct cqueue_msg *msg = q->root; |     struct cqueue_msg *msg = q->root; | ||||||
|  |  | ||||||
| @@ -107,13 +108,16 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         char selfname[TOX_MAX_NAME_LENGTH]; |         if (log->log_on) { | ||||||
|         tox_self_get_name(m, (uint8_t *) selfname); |             char selfname[TOX_MAX_NAME_LENGTH]; | ||||||
|  |             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; | ||||||
|  |  | ||||||
|  |             write_to_log(msg->message, selfname, log, msg->type == OUT_ACTION); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION); |  | ||||||
|         cqueue_mark_read(self, msg); |         cqueue_mark_read(self, msg); | ||||||
|  |  | ||||||
|         struct cqueue_msg *next = msg->next; |         struct cqueue_msg *next = msg->next; | ||||||
| @@ -123,35 +127,65 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | |||||||
|                 next->prev = NULL; |                 next->prev = NULL; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             free(msg); |  | ||||||
|             q->root = next; |             q->root = next; | ||||||
|         } else { |         } else { | ||||||
|             struct cqueue_msg *prev = msg->prev; |             struct cqueue_msg *prev = msg->prev; | ||||||
|             free(msg); |  | ||||||
|             prev->next = next; |             prev->next = next; | ||||||
|  |             next->prev = prev; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         free(msg); | ||||||
|  |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #define CQUEUE_TRY_SEND_INTERVAL 10 | // We use knowledge of toxcore internals (bad!) to determine that if we haven't received a read receipt for a | ||||||
|  | // sent packet after this amount of time, the connection has been severed and the packet needs to be re-sent. | ||||||
|  | #define TRY_SEND_TIMEOUT 32 | ||||||
|  |  | ||||||
| /* Tries to send the oldest unsent message in queue. */ | /* | ||||||
|  |  * Marks all timed out messages in queue as unsent. | ||||||
|  |  */ | ||||||
|  | static void cqueue_check_timeouts(struct cqueue_msg *msg) | ||||||
|  | { | ||||||
|  |     while (msg) { | ||||||
|  |         if (timed_out(msg->last_send_try, TRY_SEND_TIMEOUT)) { | ||||||
|  |             msg->receipt = -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         msg = msg->next; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Tries to send all messages in the send queue in sequential order. | ||||||
|  |  * If a message fails to send the function will immediately return. | ||||||
|  |  */ | ||||||
| void cqueue_try_send(ToxWindow *self, Tox *m) | void cqueue_try_send(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     struct chat_queue *q = self->chatwin->cqueue; |     struct chat_queue *q = self->chatwin->cqueue; | ||||||
|     struct cqueue_msg *msg = q->root; |     struct cqueue_msg *msg = q->root; | ||||||
|  |  | ||||||
|     if (!msg) { |     while (msg) { | ||||||
|         return; |         if (msg->receipt != -1) { | ||||||
|     } |             // we can no longer try to send unsent messages until we get receipts for our previous sent | ||||||
|  |             // messages, but we continue to iterate the list, checking timestamps for any further | ||||||
|  |             // successfully sent messages that have not yet gotten a receipt. | ||||||
|  |             cqueue_check_timeouts(msg); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|     if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) { |         TOX_ERR_FRIEND_SEND_MESSAGE err; | ||||||
|         return; |         Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION; | ||||||
|     } |         uint32_t receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, &err); | ||||||
|  |  | ||||||
|     Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION; |         if (err != TOX_ERR_FRIEND_SEND_MESSAGE_OK) { | ||||||
|     msg->receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL); |             return; | ||||||
|     msg->last_send_try = get_unix_time(); |         } | ||||||
|  |  | ||||||
|  |         msg->receipt = receipt; | ||||||
|  |         msg->last_send_try = get_unix_time(); | ||||||
|  |         msg = msg->next; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,9 +27,9 @@ struct cqueue_msg { | |||||||
|     char message[MAX_STR_SIZE]; |     char message[MAX_STR_SIZE]; | ||||||
|     size_t len; |     size_t len; | ||||||
|     int line_id; |     int line_id; | ||||||
|     uint8_t type; |  | ||||||
|     uint32_t receipt; |  | ||||||
|     time_t last_send_try; |     time_t last_send_try; | ||||||
|  |     uint8_t type; | ||||||
|  |     int64_t receipt; | ||||||
|     struct cqueue_msg *next; |     struct cqueue_msg *next; | ||||||
|     struct cqueue_msg *prev; |     struct cqueue_msg *prev; | ||||||
| }; | }; | ||||||
| @@ -42,7 +42,10 @@ struct chat_queue { | |||||||
| void cqueue_cleanup(struct chat_queue *q); | void cqueue_cleanup(struct chat_queue *q); | ||||||
| void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id); | void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id); | ||||||
|  |  | ||||||
| /* Tries to send the oldest unsent message in queue. */ | /* | ||||||
|  |  * Tries to send all messages in the send queue in sequential order. | ||||||
|  |  * If a message fails to send the function will immediately return. | ||||||
|  |  */ | ||||||
| void cqueue_try_send(ToxWindow *self, Tox *m); | void cqueue_try_send(ToxWindow *self, Tox *m); | ||||||
|  |  | ||||||
| /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | ||||||
|   | |||||||
							
								
								
									
										245
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										245
									
								
								src/misc_tools.c
									
									
									
									
									
								
							| @@ -20,46 +20,44 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <ctype.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <time.h> |  | ||||||
| #include <limits.h> |  | ||||||
| #include <dirent.h> |  | ||||||
| #include <netinet/in.h> |  | ||||||
| #include <sys/socket.h> |  | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | #include <time.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "file_transfers.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "file_transfers.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| void clear_screen(void) | void clear_screen(void) | ||||||
| { | { | ||||||
|     if (system("clear") != 0) { |     printf("\033[2J\033[1;1H"); | ||||||
|         fprintf(stderr, "Warning: system() failed to clear screen\n"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void hst_to_net(uint8_t *num, uint16_t numbytes) | void hst_to_net(uint8_t *num, uint16_t numbytes) | ||||||
| { | { | ||||||
| #ifndef WORDS_BIGENDIAN | #ifndef WORDS_BIGENDIAN | ||||||
|     uint32_t i; |     uint8_t *buff = malloc(numbytes); | ||||||
|     uint8_t buff[numbytes]; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < numbytes; ++i) { |     if (buff == NULL) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (uint32_t i = 0; i < numbytes; ++i) { | ||||||
|         buff[i] = num[numbytes - i - 1]; |         buff[i] = num[numbytes - i - 1]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memcpy(num, buff, numbytes); |     memcpy(num, buff, numbytes); | ||||||
|  |     free(buff); | ||||||
| #endif | #endif | ||||||
|     return; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| time_t get_unix_time(void) | time_t get_unix_time(void) | ||||||
| @@ -73,6 +71,22 @@ int timed_out(time_t timestamp, time_t timeout) | |||||||
|     return timestamp + timeout <= get_unix_time(); |     return timestamp + timeout <= get_unix_time(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Attempts to sleep the caller's thread for `usec` microseconds */ | ||||||
|  | void sleep_thread(long int usec) | ||||||
|  | { | ||||||
|  |     struct timespec req; | ||||||
|  |     struct timespec rem; | ||||||
|  |  | ||||||
|  |     req.tv_sec = 0; | ||||||
|  |     req.tv_nsec = usec * 1000L; | ||||||
|  |  | ||||||
|  |     if (nanosleep(&req, &rem) == -1) { | ||||||
|  |         if (nanosleep(&rem, NULL) == -1) { | ||||||
|  |             fprintf(stderr, "nanosleep() returned -1\n"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Get the current local time */ | /* Get the current local time */ | ||||||
| struct tm *get_time(void) | struct tm *get_time(void) | ||||||
| { | { | ||||||
| @@ -82,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -144,11 +163,10 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int i, res; |  | ||||||
|     const char *pos = keystr; |     const char *pos = keystr; | ||||||
|  |  | ||||||
|     for (i = 0; i < size; ++i) { |     for (size_t i = 0; i < size; ++i) { | ||||||
|         res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]); |         int res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]); | ||||||
|         pos += 2; |         pos += 2; | ||||||
|  |  | ||||||
|         if (res == EOF || res < 1) { |         if (res == EOF || res < 1) { | ||||||
| @@ -170,15 +188,31 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_ | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     size_t i; |     for (size_t i = 0; i < TOX_ADDRESS_SIZE; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < TOX_ADDRESS_SIZE; ++i) { |  | ||||||
|         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); |         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Converts a binary representation of a Tox public key into a string. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size) | ||||||
|  | { | ||||||
|  |     if (bin_pubkey_size != TOX_PUBLIC_KEY_SIZE || output_size < (TOX_PUBLIC_KEY_SIZE * 2 + 1)) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) { | ||||||
|  |         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_pubkey[i] & 0xff); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Returns 1 if the string is empty, 0 otherwise */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int string_is_empty(const char *string) | int string_is_empty(const char *string) | ||||||
| { | { | ||||||
| @@ -237,43 +271,65 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2) | |||||||
|     return strcasecmp((const char *) str1, (const char *) str2); |     return strcasecmp((const char *) str1, (const char *) str2); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | /* case-insensitive string compare function for use with qsort */ | ||||||
|       - cannot be empty | int qsort_ptr_char_array_helper(const void *str1, const void *str2) | ||||||
|       - cannot start with a space |  | ||||||
|       - must not contain a forward slash (for logfile naming purposes) |  | ||||||
|       - must not contain contiguous spaces |  | ||||||
|       - must not contain a newline or tab seqeunce */ |  | ||||||
| int valid_nick(const char *nick) |  | ||||||
| { | { | ||||||
|     if (!nick[0] || nick[0] == ' ') { |     return strcasecmp(*(char **)str1, *(char **)str2); | ||||||
|         return 0; | } | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int i; | static const char invalid_chars[] = {'/', '\n', '\t', '\v', '\r', '\0'}; | ||||||
|  |  | ||||||
|     for (i = 0; nick[i]; ++i) { | /* | ||||||
|         if ((nick[i] == ' ' && nick[i + 1] == ' ') |  * Helper function for `valid_nick()`. | ||||||
|                 || nick[i] == '/' |  * | ||||||
|                 || nick[i] == '\n' |  * Returns true if `ch` is not in the `invalid_chars` array. | ||||||
|                 || nick[i] == '\t' |  */ | ||||||
|                 || nick[i] == '\v' | static bool is_valid_char(char ch) | ||||||
|                 || nick[i] == '\r') | { | ||||||
|  |     char tmp; | ||||||
|  |  | ||||||
|         { |     for (size_t i = 0; (tmp = invalid_chars[i]); ++i) { | ||||||
|             return 0; |         if (tmp == ch) { | ||||||
|  |             return false; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 1; |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns true if nick is valid. | ||||||
|  |  * | ||||||
|  |  * A valid toxic nick: | ||||||
|  |  * - cannot be empty | ||||||
|  |  * - cannot start with a space | ||||||
|  |  * - must not contain a forward slash (for logfile naming purposes) | ||||||
|  |  * - must not contain contiguous spaces | ||||||
|  |  * - must not contain a newline or tab seqeunce | ||||||
|  |  */ | ||||||
|  | bool valid_nick(const char *nick) | ||||||
|  | { | ||||||
|  |     if (!nick[0] || nick[0] == ' ') { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; nick[i]; ++i) { | ||||||
|  |         char ch = nick[i]; | ||||||
|  |  | ||||||
|  |         if ((ch == ' ' && nick[i + 1] == ' ') || !is_valid_char(ch)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||||
| void filter_str(char *str, size_t len) | void filter_str(char *str, size_t len) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < len; ++i) { | ||||||
|  |         char ch = str[i]; | ||||||
|  |  | ||||||
|     for (i = 0; i < len; ++i) { |         if (!is_valid_char(ch) || str[i] == '\0') { | ||||||
|         if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') { |  | ||||||
|             str[i] = ' '; |             str[i] = ' '; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -377,16 +433,16 @@ on_error: | |||||||
|     return len; |     return len; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* same as get_nick_truncate but for groupchats */ | /* same as get_nick_truncate but for conferences */ | ||||||
| int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum) | int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum) | ||||||
| { | { | ||||||
|     Tox_Err_Conference_Peer_Query err; |     Tox_Err_Conference_Peer_Query err; | ||||||
|     size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err); |     size_t len = tox_conference_peer_get_name_size(m, conferencenum, peernum, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { |     if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } else { |     } else { | ||||||
|         if (!tox_conference_peer_get_name(m, groupnum, peernum, (uint8_t *) buf, NULL)) { |         if (!tox_conference_peer_get_name(m, conferencenum, peernum, (uint8_t *) buf, NULL)) { | ||||||
|             goto on_error; |             goto on_error; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -527,27 +583,6 @@ off_t file_size(const char *path) | |||||||
|     return st.st_size; |     return st.st_size; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* compares the first size bytes of fp to signature. |  | ||||||
|    Returns 0 if they are the same, 1 if they differ, and -1 on error. |  | ||||||
|  |  | ||||||
|    On success this function will seek back to the beginning of fp */ |  | ||||||
| int check_file_signature(const unsigned char *signature, size_t size, FILE *fp) |  | ||||||
| { |  | ||||||
|     char buf[size]; |  | ||||||
|  |  | ||||||
|     if (fread(buf, size, 1, fp) != 1) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int ret = memcmp(signature, buf, size); |  | ||||||
|  |  | ||||||
|     if (fseek(fp, 0L, SEEK_SET) == -1) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ret == 0 ? 0 : 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* sets window title in tab bar. */ | /* sets window title in tab bar. */ | ||||||
| void set_window_title(ToxWindow *self, const char *title, int len) | void set_window_title(ToxWindow *self, const char *title, int len) | ||||||
| { | { | ||||||
| @@ -557,8 +592,8 @@ 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->is_groupchat) { /* keep groupnumber in title */ |     if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */ | ||||||
|         snprintf(cpy, sizeof(cpy), "%d %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); | ||||||
|     } |     } | ||||||
| @@ -571,35 +606,49 @@ void set_window_title(ToxWindow *self, const char *title, int len) | |||||||
|     snprintf(self->name, sizeof(self->name), "%s", cpy); |     snprintf(self->name, sizeof(self->name), "%s", cpy); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Return true if address appears to be a valid ipv4 address. */ | /* | ||||||
| bool is_ip4_address(const char *address) |  * Frees all members of a pointer array plus `arr`. | ||||||
|  |  */ | ||||||
|  | void free_ptr_array(void **arr) | ||||||
| { | { | ||||||
|     struct sockaddr_in s_addr; |     if (arr == NULL) { | ||||||
|     return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void **tmp = arr; | ||||||
|  |  | ||||||
|  |     while (*arr) { | ||||||
|  |         free(*arr); | ||||||
|  |         ++arr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     free(tmp); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Return true if address roughly appears to be a valid ipv6 address. | /* | ||||||
|  |  * Returns a null terminated array of `length` pointers. Each pointer is allocated `bytes` bytes. | ||||||
|  |  * Returns NULL on failure. | ||||||
|  * |  * | ||||||
|  * TODO: Improve this function (inet_pton behaves strangely with ipv6). |  * The caller is responsible for freeing the array with `free_ptr_array`. | ||||||
|  * for now the only guarantee is that it won't return true if the |  | ||||||
|  * address is a domain or ipv4 address, and should only be used if you're |  | ||||||
|  * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). |  | ||||||
|  */ |  */ | ||||||
| bool is_ip6_address(const char *address) | void **malloc_ptr_array(size_t length, size_t bytes) | ||||||
| { | { | ||||||
|     size_t i; |     void **arr = malloc((length + 1) * sizeof(void *)); | ||||||
|     size_t num_colons = 0; |  | ||||||
|     char ch = 0; |  | ||||||
|  |  | ||||||
|     for (i = 0; (ch = address[i]); ++i) { |     if (arr == NULL) { | ||||||
|         if (ch == '.') { |         return NULL; | ||||||
|             return false; |     } | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (ch == ':') { |     for (size_t i = 0; i < length; ++i) { | ||||||
|             ++num_colons; |         arr[i] = malloc(bytes); | ||||||
|  |  | ||||||
|  |         if (arr[i] == NULL) { | ||||||
|  |             free_ptr_array(arr); | ||||||
|  |             return NULL; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return num_colons > 1 && num_colons < 8; |     arr[length] = NULL; | ||||||
|  |  | ||||||
|  |     return arr; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,8 +24,8 @@ | |||||||
|  |  | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| #ifndef MIN | #ifndef MIN | ||||||
| #define MIN(x, y) (((x) < (y)) ? (x) : (y)) | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) | ||||||
| @@ -71,11 +71,18 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr); | |||||||
|  */ |  */ | ||||||
| int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size); | int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size); | ||||||
|  |  | ||||||
|  | /* Converts a binary representation of a Tox public key into a string. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size); | ||||||
|  |  | ||||||
| /* 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); | ||||||
| @@ -101,19 +108,28 @@ 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); | ||||||
|  |  | ||||||
|  | /* Attempts to sleep the caller's thread for `usec` microseconds */ | ||||||
|  | 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 */ | ||||||
| void alert_window(ToxWindow *self, int type, bool is_beep); | void alert_window(ToxWindow *self, int type, bool is_beep); | ||||||
|  |  | ||||||
| /* case-insensitive string compare function for use with qsort */ | /* case-insensitive string compare function for use with qsort */ | ||||||
| int qsort_strcasecmp_hlpr(const void *str1, const void *str2); | int qsort_strcasecmp_hlpr(const void *str1, const void *str2); | ||||||
|  |  | ||||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | /* case-insensitive string compare function for use with qsort */ | ||||||
|       - cannot be empty | int qsort_ptr_char_array_helper(const void *str1, const void *str2); | ||||||
|       - cannot start with a space |  | ||||||
|       - must not contain a forward slash (for logfile naming purposes) | /* Returns true if nick is valid. | ||||||
|       - must not contain contiguous spaces |  * | ||||||
|       - must not contain a newline or tab seqeunce */ |  * A valid toxic nick: | ||||||
| int valid_nick(const char *nick); |  * - cannot be empty | ||||||
|  |  * - cannot start with a space | ||||||
|  |  * - must not contain a forward slash (for logfile naming purposes) | ||||||
|  |  * - must not contain contiguous spaces | ||||||
|  |  * - must not contain a newline or tab seqeunce | ||||||
|  |  */ | ||||||
|  | bool valid_nick(const char *nick); | ||||||
|  |  | ||||||
| /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||||
| void filter_str(char *str, size_t len); | void filter_str(char *str, size_t len); | ||||||
| @@ -136,8 +152,8 @@ void str_to_lower(char *str); | |||||||
|    Returns nick len on success, -1 on failure */ |    Returns nick len on success, -1 on failure */ | ||||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | ||||||
|  |  | ||||||
| /* same as get_nick_truncate but for groupchats */ | /* same as get_nick_truncate but for conferences */ | ||||||
| int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum); | int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum); | ||||||
|  |  | ||||||
| /* copies data to msg buffer. | /* copies data to msg buffer. | ||||||
|    returns length of msg, which will be no larger than size-1 */ |    returns length of msg, which will be no larger than size-1 */ | ||||||
| @@ -169,25 +185,20 @@ File_Type file_type(const char *path); | |||||||
| /* returns file size. If file doesn't exist returns 0. */ | /* returns file size. If file doesn't exist returns 0. */ | ||||||
| off_t file_size(const char *path); | off_t file_size(const char *path); | ||||||
|  |  | ||||||
| /* compares the first size bytes of fp and signature. |  | ||||||
|    Returns 0 if they are the same, 1 if they differ, and -1 on error. |  | ||||||
|  |  | ||||||
|    On success this function will seek back to the beginning of fp */ |  | ||||||
| int check_file_signature(const unsigned char *signature, size_t size, FILE *fp); |  | ||||||
|  |  | ||||||
| /* sets window title in tab bar. */ | /* sets window title in tab bar. */ | ||||||
| void set_window_title(ToxWindow *self, const char *title, int len); | void set_window_title(ToxWindow *self, const char *title, int len); | ||||||
|  |  | ||||||
| /* Return true if address appears to be a valid ipv4 address. */ | /* | ||||||
| bool is_ip4_address(const char *address); |  * Frees all members of a pointer array plus `arr`. | ||||||
|  |  | ||||||
| /* Return true if address roughly appears to be a valid ipv6 address. |  | ||||||
|  * |  | ||||||
|  * TODO: Improve this function (inet_pton behaves strangely with ipv6). |  | ||||||
|  * for now the only guarantee is that it won't return true if the |  | ||||||
|  * address is a domain or ipv4 address, and should only be used if you're |  | ||||||
|  * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). |  | ||||||
|  */ |  */ | ||||||
| bool is_ip6_address(const char *address); | void free_ptr_array(void **arr); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Returns a null terminated array of `length` pointers. Each pointer is allocated `bytes` bytes. | ||||||
|  |  * Returns NULL on failure. | ||||||
|  |  * | ||||||
|  |  * The caller is responsible for freeing the array with `free_ptr_array`. | ||||||
|  |  */ | ||||||
|  | void **malloc_ptr_array(size_t length, size_t bytes); | ||||||
|  |  | ||||||
| #endif /* MISC_TOOLS_H */ | #endif /* MISC_TOOLS_H */ | ||||||
|   | |||||||
| @@ -20,18 +20,18 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <curl/curl.h> | #include <curl/curl.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "global_commands.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
| #include "curl_util.h" | #include "curl_util.h" | ||||||
|  | #include "global_commands.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern struct arg_opts arg_opts; | extern struct arg_opts arg_opts; | ||||||
| extern struct Winthread Winthread; | extern struct Winthread Winthread; | ||||||
| @@ -63,6 +63,13 @@ static struct lookup_thread { | |||||||
|     pthread_attr_t attr; |     pthread_attr_t attr; | ||||||
| } lookup_thread; | } lookup_thread; | ||||||
|  |  | ||||||
|  | static void clear_thread_data(void) | ||||||
|  | { | ||||||
|  |     t_data = (struct thread_data) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int lookup_error(ToxWindow *self, const char *errmsg, ...) | static int lookup_error(ToxWindow *self, const char *errmsg, ...) | ||||||
| { | { | ||||||
|     char frmt_msg[MAX_STR_SIZE]; |     char frmt_msg[MAX_STR_SIZE]; | ||||||
| @@ -73,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; | ||||||
| @@ -81,7 +88,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...) | |||||||
|  |  | ||||||
| static void kill_lookup_thread(void) | static void kill_lookup_thread(void) | ||||||
| { | { | ||||||
|     memset(&t_data, 0, sizeof(struct thread_data)); |     clear_thread_data(); | ||||||
|     pthread_attr_destroy(&lookup_thread.attr); |     pthread_attr_destroy(&lookup_thread.attr); | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| } | } | ||||||
| @@ -265,11 +272,14 @@ void *lookup_thread_func(void *data) | |||||||
|         kill_lookup_thread(); |         kill_lookup_thread(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct Recv_Curl_Data recv_data; |     struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data)); | ||||||
|  |  | ||||||
|     memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); |     if (recv_data == NULL) { | ||||||
|  |         lookup_error(self, "memory allocation error"); | ||||||
|  |         kill_lookup_thread(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char post_data[MAX_STR_SIZE]; |     char post_data[MAX_STR_SIZE + 30]; | ||||||
|  |  | ||||||
|     snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name); |     snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name); | ||||||
|  |  | ||||||
| @@ -285,7 +295,7 @@ void *lookup_thread_func(void *data) | |||||||
|  |  | ||||||
|     curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data); |     curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data); | ||||||
|  |  | ||||||
|     curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &recv_data); |     curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data); | ||||||
|  |  | ||||||
|     curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); |     curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | ||||||
|  |  | ||||||
| @@ -334,7 +344,7 @@ void *lookup_thread_func(void *data) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (process_response(&recv_data) == -1) { |     if (process_response(recv_data) == -1) { | ||||||
|         lookup_error(self, "Bad response."); |         lookup_error(self, "Bad response."); | ||||||
|         goto on_exit; |         goto on_exit; | ||||||
|     } |     } | ||||||
| @@ -344,6 +354,7 @@ void *lookup_thread_func(void *data) | |||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
| on_exit: | on_exit: | ||||||
|  |     free(recv_data); | ||||||
|     curl_slist_free_all(headers); |     curl_slist_free_all(headers); | ||||||
|     curl_easy_cleanup(c_handle); |     curl_easy_cleanup(c_handle); | ||||||
|     kill_lookup_thread(); |     kill_lookup_thread(); | ||||||
| @@ -354,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -371,22 +382,22 @@ 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"); | ||||||
|         memset(&t_data, 0, sizeof(struct 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); | ||||||
|         memset(&t_data, 0, sizeof(struct 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); | ||||||
|         memset(&t_data, 0, sizeof(struct thread_data)); |         clear_thread_data(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										143
									
								
								src/notify.c
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								src/notify.c
									
									
									
									
									
								
							| @@ -20,22 +20,22 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <assert.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include <time.h> |  | ||||||
| #include <assert.h> |  | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "notify.h" |  | ||||||
| #include "audio_device.h" | #include "audio_device.h" | ||||||
| #include "settings.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "xtra.h" | #include "notify.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "x11focus.h" | ||||||
|  |  | ||||||
| #if defined(AUDIO) || defined(SOUND_NOTIFY) | #if defined(AUDIO) || defined(SOUND_NOTIFY) | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
| @@ -60,7 +60,7 @@ | |||||||
|  |  | ||||||
| #define MAX_BOX_MSG_LEN 127 | #define MAX_BOX_MSG_LEN 127 | ||||||
| #define SOUNDS_SIZE 10 | #define SOUNDS_SIZE 10 | ||||||
| #define ACTIVE_NOTIFS_MAX 50 | #define ACTIVE_NOTIFS_MAX 10 | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| @@ -101,6 +101,17 @@ static struct _ActiveNotifications { | |||||||
| /**********************************************************************************/ | /**********************************************************************************/ | ||||||
| /**********************************************************************************/ | /**********************************************************************************/ | ||||||
|  |  | ||||||
|  | static void clear_actives_index(size_t idx) | ||||||
|  | { | ||||||
|  |     if (actives[idx].id_indicator) { | ||||||
|  |         *actives[idx].id_indicator = -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     actives[idx] = (struct _ActiveNotifications) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* coloured tab notifications: primary notification type */ | /* coloured tab notifications: primary notification type */ | ||||||
| static void tab_notify(ToxWindow *self, uint64_t flags) | static void tab_notify(ToxWindow *self, uint64_t flags) | ||||||
| { | { | ||||||
| @@ -115,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) | ||||||
| @@ -169,7 +182,7 @@ void m_open_device(void) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Blah error check */ |     /* Blah error check */ | ||||||
|     open_primary_device(output, &Control.device_idx, 48000, 20, 1); |     open_output_device(&Control.device_idx, 48000, 20, 1); | ||||||
|  |  | ||||||
|     device_opened = true; |     device_opened = true; | ||||||
| } | } | ||||||
| @@ -193,7 +206,7 @@ void graceful_clear(void) | |||||||
|     while (1) { |     while (1) { | ||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |         for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|             if (actives[i].active) { |             if (actives[i].active) { | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
| @@ -213,7 +226,7 @@ void graceful_clear(void) | |||||||
|                     stop_sound(i); |                     stop_sound(i); | ||||||
|                 } else { |                 } else { | ||||||
|                     if (!is_playing(actives[i].source)) { |                     if (!is_playing(actives[i].source)) { | ||||||
|                         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |                         clear_actives_index(i); | ||||||
|                     } else { |                     } else { | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
| @@ -227,7 +240,7 @@ void graceful_clear(void) | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         usleep(1000); |         sleep_thread(1000L); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     control_unlock(); |     control_unlock(); | ||||||
| @@ -249,7 +262,7 @@ void *do_playing(void *_p) | |||||||
|         bool test_active_notify = false; |         bool test_active_notify = false; | ||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |         for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|  |  | ||||||
|             if (actives[i].looping) { |             if (actives[i].looping) { | ||||||
|                 has_looping = true; |                 has_looping = true; | ||||||
| @@ -270,7 +283,7 @@ void *do_playing(void *_p) | |||||||
|                     alSourceStop(actives[i].source); |                     alSourceStop(actives[i].source); | ||||||
|                     alDeleteSources(1, &actives[i].source); |                     alDeleteSources(1, &actives[i].source); | ||||||
|                     alDeleteBuffers(1, &actives[i].buffer); |                     alDeleteBuffers(1, &actives[i].buffer); | ||||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |                     clear_actives_index(i); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -289,7 +302,7 @@ void *do_playing(void *_p) | |||||||
|                     alSourceStop(actives[i].source); |                     alSourceStop(actives[i].source); | ||||||
|                     alDeleteSources(1, &actives[i].source); |                     alDeleteSources(1, &actives[i].source); | ||||||
|                     alDeleteBuffers(1, &actives[i].buffer); |                     alDeleteBuffers(1, &actives[i].buffer); | ||||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |                     clear_actives_index(i); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -305,7 +318,7 @@ void *do_playing(void *_p) | |||||||
|         has_looping = false; |         has_looping = false; | ||||||
|  |  | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         usleep(10000); |         sleep_thread(10000L); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| @@ -315,7 +328,7 @@ int play_source(uint32_t source, uint32_t buffer, bool looping) | |||||||
| { | { | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++); |     for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; ++i); | ||||||
|  |  | ||||||
|     if (i == ACTIVE_NOTIFS_MAX) { |     if (i == ACTIVE_NOTIFS_MAX) { | ||||||
|         return -1; /* Full */ |         return -1; /* Full */ | ||||||
| @@ -344,24 +357,16 @@ void *do_playing(void *_p) | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         int i; |         for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |  | ||||||
|             if (actives[i].box && time(NULL) >= actives[i].n_timeout) { |             if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||||
|                 GError *ignore; |                 GError *ignore; | ||||||
|                 notify_notification_close(actives[i].box, &ignore); |                 notify_notification_close(actives[i].box, &ignore); | ||||||
|                 actives[i].box = NULL; |                 clear_actives_index(i); | ||||||
|  |  | ||||||
|                 if (actives[i].id_indicator) { |  | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         usleep(10000); |         sleep_thread(10000L); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| @@ -369,27 +374,48 @@ void *do_playing(void *_p) | |||||||
|  |  | ||||||
| void graceful_clear(void) | void graceful_clear(void) | ||||||
| { | { | ||||||
|     int i; |  | ||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
|     for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |     for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|         if (actives[i].box) { |         if (actives[i].box) { | ||||||
|             GError *ignore; |             GError *ignore; | ||||||
|             notify_notification_close(actives[i].box, &ignore); |             notify_notification_close(actives[i].box, &ignore); | ||||||
|             actives[i].box = NULL; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (actives[i].id_indicator) { |         clear_actives_index(i); | ||||||
|             *actives[i].id_indicator = -1;    /* reset indicator value */ |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     control_unlock(); |     control_unlock(); | ||||||
| } | } | ||||||
|  |  | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
|  | /* Kills all notifications for `id`. This must be called before freeing a ToxWindow. */ | ||||||
|  | void kill_notifs(int id) | ||||||
|  | { | ||||||
|  |     control_lock(); | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|  |         if (!actives[i].id_indicator) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (*actives[i].id_indicator == id) { | ||||||
|  | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
|  |             if (actives[i].box) { | ||||||
|  |                 GError *ignore; | ||||||
|  |                 notify_notification_close(actives[i].box, &ignore); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  | #endif // BOX_NOTIFY | ||||||
|  |             clear_actives_index(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     control_unlock(); | ||||||
|  | } | ||||||
|  |  | ||||||
| /**********************************************************************************/ | /**********************************************************************************/ | ||||||
| /**********************************************************************************/ | /**********************************************************************************/ | ||||||
| /**********************************************************************************/ | /**********************************************************************************/ | ||||||
| @@ -450,7 +476,7 @@ void terminate_notify(void) | |||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     for (; i < SOUNDS_SIZE; i ++) { |     for (; i < SOUNDS_SIZE; ++i) { | ||||||
|         free(Control.sounds[i]); |         free(Control.sounds[i]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -523,7 +549,6 @@ int play_notify_sound(Notification notif, uint64_t flags) | |||||||
|     return rc; |     return rc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void stop_sound(int id) | void stop_sound(int id) | ||||||
| { | { | ||||||
|     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) { |     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) { | ||||||
| @@ -536,15 +561,11 @@ void stop_sound(int id) | |||||||
|  |  | ||||||
| #endif /* BOX_NOTIFY */ | #endif /* BOX_NOTIFY */ | ||||||
|  |  | ||||||
|         if (actives[id].id_indicator) { |  | ||||||
|             *actives[id].id_indicator = -1; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // alSourcei(actives[id].source, AL_LOOPING, false); |         // alSourcei(actives[id].source, AL_LOOPING, false); | ||||||
|         alSourceStop(actives[id].source); |         alSourceStop(actives[id].source); | ||||||
|         alDeleteSources(1, &actives[id].source); |         alDeleteSources(1, &actives[id].source); | ||||||
|         alDeleteBuffers(1, &actives[id].buffer); |         alDeleteBuffers(1, &actives[id].buffer); | ||||||
|         memset(&actives[id], 0, sizeof(struct _ActiveNotifications)); |         clear_actives_index(id); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| @@ -754,20 +775,18 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con | |||||||
|     actives[id].size++; |     actives[id].size++; | ||||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; |     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||||
|  |  | ||||||
|     char formated[128 * 129] = {'\0'}; |     char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2))); | ||||||
|  |  | ||||||
|     int i = 0; |     for (size_t i = 0; i < actives[id].size; ++i) { | ||||||
|  |         strcat(formatted, actives[id].messages[i]); | ||||||
|     for (; i < actives[id].size; i ++) { |         strcat(formatted, "\n"); | ||||||
|         strcat(formated, actives[id].messages[i]); |  | ||||||
|         strcat(formated, "\n"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     formated[strlen(formated) - 1] = '\0'; |     notify_notification_update(actives[id].box, actives[id].title, formatted, NULL); | ||||||
|  |  | ||||||
|     notify_notification_update(actives[id].box, actives[id].title, formated, NULL); |  | ||||||
|     notify_notification_show(actives[id].box, NULL); |     notify_notification_show(actives[id].box, NULL); | ||||||
|  |  | ||||||
|  |     free(formatted); | ||||||
|  |  | ||||||
|     control_unlock(); |     control_unlock(); | ||||||
|  |  | ||||||
|     return id; |     return id; | ||||||
| @@ -863,20 +882,18 @@ int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *form | |||||||
|     actives[id].size ++; |     actives[id].size ++; | ||||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; |     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||||
|  |  | ||||||
|     char formated[128 * 129] = {'\0'}; |     char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2))); | ||||||
|  |  | ||||||
|     int i = 0; |     for (size_t i = 0; i < actives[id].size; ++i) { | ||||||
|  |         strcat(formatted, actives[id].messages[i]); | ||||||
|     for (; i < actives[id].size; i ++) { |         strcat(formatted, "\n"); | ||||||
|         strcat(formated, actives[id].messages[i]); |  | ||||||
|         strcat(formated, "\n"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     formated[strlen(formated) - 1] = '\0'; |     notify_notification_update(actives[id].box, actives[id].title, formatted, NULL); | ||||||
|  |  | ||||||
|     notify_notification_update(actives[id].box, actives[id].title, formated, NULL); |  | ||||||
|     notify_notification_show(actives[id].box, NULL); |     notify_notification_show(actives[id].box, NULL); | ||||||
|  |  | ||||||
|  |     free(formatted); | ||||||
|  |  | ||||||
|     control_unlock(); |     control_unlock(); | ||||||
|  |  | ||||||
|     return id; |     return id; | ||||||
|   | |||||||
| @@ -62,6 +62,9 @@ typedef enum _Flags { | |||||||
| int init_notify(int login_cooldown, int notification_timeout); | int init_notify(int login_cooldown, int notification_timeout); | ||||||
| void terminate_notify(void); | void terminate_notify(void); | ||||||
|  |  | ||||||
|  | /* Kills all notifications for `id`. This must be called before freeing a ToxWindow. */ | ||||||
|  | void kill_notifs(int id); | ||||||
|  |  | ||||||
| int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator); | int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator); | ||||||
| int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); | int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										280
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										280
									
								
								src/prompt.c
									
									
									
									
									
								
							| @@ -28,20 +28,20 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <wchar.h> | #include <wchar.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "prompt.h" |  | ||||||
| #include "friendlist.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "toxic_strings.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "input.h" |  | ||||||
| #include "help.h" |  | ||||||
| #include "notify.h" |  | ||||||
| #include "autocomplete.h" | #include "autocomplete.h" | ||||||
|  | #include "execute.h" | ||||||
|  | #include "friendlist.h" | ||||||
|  | #include "help.h" | ||||||
|  | #include "input.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "prompt.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
| @@ -49,68 +49,47 @@ extern struct Winthread Winthread; | |||||||
|  |  | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
| FriendRequests FrndRequests; | FriendRequests FrndRequests; | ||||||
| #ifdef AUDIO |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_AUDIO 2 |  | ||||||
| #else |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_AUDIO 0 |  | ||||||
| #endif /* AUDIO */ |  | ||||||
| #ifdef VIDEO |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_VIDEO 2 |  | ||||||
| #else |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_VIDEO 0 |  | ||||||
| #endif /* VIDEO */ |  | ||||||
| #ifdef PYTHON |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_PYTHON 1 |  | ||||||
| #else |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_PYTHON 0 |  | ||||||
| #endif /* PYTHON */ |  | ||||||
| #ifdef QRCODE |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_QRCODE 1 |  | ||||||
| #else |  | ||||||
| #define AC_NUM_GLOB_COMMANDS_QRCODE 0 |  | ||||||
| #endif /* QRCODE */ |  | ||||||
| #define AC_NUM_GLOB_COMMANDS (17 + AC_NUM_GLOB_COMMANDS_AUDIO + AC_NUM_GLOB_COMMANDS_VIDEO + AC_NUM_GLOB_COMMANDS_PYTHON + AC_NUM_GLOB_COMMANDS_QRCODE) |  | ||||||
|  |  | ||||||
| /* Array of global command names used for tab completion. */ | /* Array of global command names used for tab completion. */ | ||||||
| static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | static const char *glob_cmd_list[] = { | ||||||
|     { "/accept"     }, |     "/accept", | ||||||
|     { "/add"        }, |     "/add", | ||||||
|     { "/avatar"     }, |     "/avatar", | ||||||
|     { "/clear"      }, |     "/clear", | ||||||
|     { "/connect"    }, |     "/connect", | ||||||
|     { "/decline"    }, |     "/decline", | ||||||
|     { "/exit"       }, |     "/exit", | ||||||
|     { "/group"      }, |     "/conference", | ||||||
|     { "/help"       }, |     "/help", | ||||||
|     { "/log"        }, |     "/log", | ||||||
|     { "/myid"       }, |     "/myid", | ||||||
| #ifdef QRCODE | #ifdef QRCODE | ||||||
|     { "/myqr"       }, |     "/myqr", | ||||||
| #endif /* QRCODE */ | #endif /* QRCODE */ | ||||||
|     { "/nick"       }, |     "/nick", | ||||||
|     { "/note"       }, |     "/note", | ||||||
|     { "/nospam"     }, |     "/nospam", | ||||||
|     { "/quit"       }, |     "/quit", | ||||||
|     { "/requests"   }, |     "/requests", | ||||||
|     { "/status"     }, |     "/status", | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|     { "/lsdev"       }, |     "/lsdev", | ||||||
|     { "/sdev"        }, |     "/sdev", | ||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|  |  | ||||||
|     { "/lsvdev"      }, |     "/lsvdev", | ||||||
|     { "/svdev"       }, |     "/svdev", | ||||||
|  |  | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|  |  | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
|  |  | ||||||
|     { "/run"         }, |     "/run", | ||||||
|  |  | ||||||
| #endif /* PYTHON */ | #endif /* PYTHON */ | ||||||
|  |  | ||||||
| @@ -165,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); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -272,14 +251,14 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
|             else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { |             else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { | ||||||
|                 const char status_cmd_list[3][8] = { |                 const char *status_cmd_list[] = { | ||||||
|                     {"online"}, |                     "online", | ||||||
|                     {"away"}, |                     "away", | ||||||
|                     {"busy"}, |                     "busy", | ||||||
|                 }; |                 }; | ||||||
|                 diff = complete_line(self, status_cmd_list, 3, 8); |                 diff = complete_line(self, status_cmd_list, sizeof(status_cmd_list) / sizeof(char *)); | ||||||
|             } else { |             } else { | ||||||
|                 diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); |                 diff = complete_line(self, glob_cmd_list, sizeof(glob_cmd_list) / sizeof(char *)); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (diff != -1) { |             if (diff != -1) { | ||||||
| @@ -302,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); | ||||||
|  |                 execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); |  | ||||||
|             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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -322,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) { | ||||||
| @@ -337,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]; | ||||||
| @@ -422,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); | ||||||
|  |  | ||||||
| @@ -455,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) { | ||||||
| @@ -465,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) { | ||||||
| @@ -477,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) { | ||||||
| @@ -497,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); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -556,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) { | ||||||
| @@ -586,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)); | ||||||
| @@ -598,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); | ||||||
| @@ -624,7 +646,7 @@ ToxWindow *new_prompt(void) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret->num = -1; |     ret->num = -1; | ||||||
|     ret->is_prompt = true; |     ret->type = WINDOW_TYPE_PROMPT; | ||||||
|  |  | ||||||
|     ret->onKey = &prompt_onKey; |     ret->onKey = &prompt_onKey; | ||||||
|     ret->onDraw = &prompt_onDraw; |     ret->onDraw = &prompt_onDraw; | ||||||
| @@ -632,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)); | ||||||
|   | |||||||
| @@ -26,22 +26,20 @@ | |||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| #define MAX_FRIEND_REQUESTS 32 | #define MAX_FRIEND_REQUESTS 20 | ||||||
|  |  | ||||||
| struct friend_request { | struct friend_request { | ||||||
|     bool active; |     bool active; | ||||||
|     char msg[MAX_STR_SIZE]; |     char msg[TOX_MAX_FRIEND_REQUEST_LENGTH + 1]; | ||||||
|     uint8_t key[TOX_PUBLIC_KEY_SIZE]; |     uint8_t key[TOX_PUBLIC_KEY_SIZE]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct FriendRequests { | ||||||
|     int max_idx; |     int max_idx; | ||||||
|     int num_requests; |     int num_requests; | ||||||
|     struct friend_request request[MAX_FRIEND_REQUESTS]; |     struct friend_request request[MAX_FRIEND_REQUESTS]; | ||||||
| } FriendRequests; | } FriendRequests; | ||||||
|  |  | ||||||
| extern FriendRequests FrndRequests; |  | ||||||
|  |  | ||||||
| ToxWindow *new_prompt(void); | ToxWindow *new_prompt(void); | ||||||
|  |  | ||||||
| void prep_prompt_win(void); | void prep_prompt_win(void); | ||||||
|   | |||||||
| @@ -114,25 +114,23 @@ 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, "")) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -252,26 +250,26 @@ PyMODINIT_FUNC PyInit_toxic_api(void) | |||||||
|     PyObject *m = PyModule_Create(&toxic_api_module); |     PyObject *m = PyModule_Create(&toxic_api_module); | ||||||
|     PyObject *global_command_const    = Py_BuildValue("i", GLOBAL_COMMAND_MODE); |     PyObject *global_command_const    = Py_BuildValue("i", GLOBAL_COMMAND_MODE); | ||||||
|     PyObject *chat_command_const      = Py_BuildValue("i", CHAT_COMMAND_MODE); |     PyObject *chat_command_const      = Py_BuildValue("i", CHAT_COMMAND_MODE); | ||||||
|     PyObject *groupchat_command_const = Py_BuildValue("i", GROUPCHAT_COMMAND_MODE); |     PyObject *conference_command_const = Py_BuildValue("i", CONFERENCE_COMMAND_MODE); | ||||||
|     PyObject_SetAttrString(m, "GLOBAL_COMMAND",    global_command_const); |     PyObject_SetAttrString(m, "GLOBAL_COMMAND",    global_command_const); | ||||||
|     PyObject_SetAttrString(m, "CHAT_COMMAND",      chat_command_const); |     PyObject_SetAttrString(m, "CHAT_COMMAND",      chat_command_const); | ||||||
|     PyObject_SetAttrString(m, "GROUPCHAT_COMMAND", groupchat_command_const); |     PyObject_SetAttrString(m, "CONFERENCE_COMMAND", conference_command_const); | ||||||
|     Py_DECREF(global_command_const); |     Py_DECREF(global_command_const); | ||||||
|     Py_DECREF(chat_command_const); |     Py_DECREF(chat_command_const); | ||||||
|     Py_DECREF(groupchat_command_const); |     Py_DECREF(conference_command_const); | ||||||
|     return m; |     return m; | ||||||
| } | } | ||||||
|  |  | ||||||
| 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); | ||||||
|   | |||||||
| @@ -22,14 +22,14 @@ | |||||||
|  |  | ||||||
| #ifdef QRCODE | #ifdef QRCODE | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <qrencode.h> | #include <qrencode.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "qr_code.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "qr_code.h" |  | ||||||
|  |  | ||||||
| #ifdef QRPNG | #ifdef QRPNG | ||||||
| #include <png.h> | #include <png.h> | ||||||
| @@ -138,12 +138,19 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile) | |||||||
|  |  | ||||||
|     real_width = (qr_obj->width + BORDER_LEN * 2) * SQUARE_SIZE; |     real_width = (qr_obj->width + BORDER_LEN * 2) * SQUARE_SIZE; | ||||||
|     size_t row_size = real_width * 4; |     size_t row_size = real_width * 4; | ||||||
|     unsigned char row[row_size]; |     unsigned char *row = malloc(row_size); | ||||||
|  |  | ||||||
|  |     if (row == NULL) { | ||||||
|  |         fclose(fp); | ||||||
|  |         QRcode_free(qr_obj); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); |     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | ||||||
|  |  | ||||||
|     if (png_ptr == NULL) { |     if (png_ptr == NULL) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|  |         free(row); | ||||||
|         QRcode_free(qr_obj); |         QRcode_free(qr_obj); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -152,12 +159,14 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile) | |||||||
|  |  | ||||||
|     if (info_ptr == NULL) { |     if (info_ptr == NULL) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|  |         free(row); | ||||||
|         QRcode_free(qr_obj); |         QRcode_free(qr_obj); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (setjmp(png_jmpbuf(png_ptr))) { |     if (setjmp(png_jmpbuf(png_ptr))) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|  |         free(row); | ||||||
|         QRcode_free(qr_obj); |         QRcode_free(qr_obj); | ||||||
|         png_destroy_write_struct(&png_ptr, &info_ptr); |         png_destroy_write_struct(&png_ptr, &info_ptr); | ||||||
|         return -1; |         return -1; | ||||||
| @@ -206,10 +215,12 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile) | |||||||
|         png_write_row(png_ptr, row); |         png_write_row(png_ptr, row); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     free(row); | ||||||
|  |     fclose(fp); | ||||||
|  |  | ||||||
|     png_write_end(png_ptr, info_ptr); |     png_write_end(png_ptr, info_ptr); | ||||||
|     png_destroy_write_struct(&png_ptr, &info_ptr); |     png_destroy_write_struct(&png_ptr, &info_ptr); | ||||||
|  |  | ||||||
|     fclose(fp); |  | ||||||
|     QRcode_free(qr_obj); |     QRcode_free(qr_obj); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
| @@ -20,23 +20,23 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <libconfig.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <libconfig.h> |  | ||||||
| #include <ctype.h> |  | ||||||
|  |  | ||||||
|  | #include "configdir.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "configdir.h" |  | ||||||
| #include "notify.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| #include "audio_device.h" | #include "audio_device.h" | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #include "settings.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
|  | #include "settings.h" | ||||||
|  |  | ||||||
| #ifndef PACKAGE_DATADIR | #ifndef PACKAGE_DATADIR | ||||||
| #define PACKAGE_DATADIR "." | #define PACKAGE_DATADIR "." | ||||||
| @@ -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 { | ||||||
| @@ -204,19 +210,28 @@ static const struct audio_strings { | |||||||
|     const char *self; |     const char *self; | ||||||
|     const char *input_device; |     const char *input_device; | ||||||
|     const char *output_device; |     const char *output_device; | ||||||
|     const char *VAD_treshold; |     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_treshold", |     "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) | ||||||
| { | { | ||||||
|     settings->audio_in_dev = 0; |     settings->audio_in_dev = 0; | ||||||
|     settings->audio_out_dev = 0; |     settings->audio_out_dev = 0; | ||||||
|     settings->VAD_treshold = 40.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); | ||||||
|         } |         } | ||||||
| @@ -505,7 +536,16 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|         config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev); |         config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev); | ||||||
|         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_treshold, &s->VAD_treshold); |         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; | ||||||
| @@ -84,7 +89,10 @@ struct user_settings { | |||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     int audio_in_dev; |     int audio_in_dev; | ||||||
|     int audio_out_dev; |     int audio_out_dev; | ||||||
|     double VAD_treshold; |     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); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,18 +26,18 @@ | |||||||
| #include <string.h> /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp */ | #include <string.h> /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp */ | ||||||
|  |  | ||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | #include <sys/types.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| #include <tox/tox.h> | #include <tox/tox.h> | ||||||
|  |  | ||||||
| #include "execute.h" | #include "execute.h" | ||||||
| #include "windows.h" | #include "settings.h" | ||||||
| #include "term_mplex.h" | #include "term_mplex.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "settings.h" | #include "windows.h" | ||||||
|  |  | ||||||
| extern struct ToxWindow *prompt; | extern struct ToxWindow *prompt; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
| @@ -100,9 +100,19 @@ static char *read_into_dyn_buffer(FILE *stream) | |||||||
|         int length = dyn_buffer_size + strlen(input_ptr); |         int length = dyn_buffer_size + strlen(input_ptr); | ||||||
|  |  | ||||||
|         if (dyn_buffer) { |         if (dyn_buffer) { | ||||||
|             dyn_buffer = (char *) realloc(dyn_buffer, length); |             char *tmp = realloc(dyn_buffer, length); | ||||||
|  |  | ||||||
|  |             if (tmp == NULL) { | ||||||
|  |                 return NULL; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             dyn_buffer = tmp; | ||||||
|         } else { |         } else { | ||||||
|             dyn_buffer = (char *) malloc(length); |             dyn_buffer = malloc(length); | ||||||
|  |  | ||||||
|  |             if (dyn_buffer == NULL) { | ||||||
|  |                 return NULL; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         strcpy(dyn_buffer + dyn_buffer_size - 1, input_ptr); |         strcpy(dyn_buffer + dyn_buffer_size - 1, input_ptr); | ||||||
| @@ -143,7 +153,12 @@ static char *extract_socket_path(const char *info) | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     path = (char *) malloc(end - pos + 1); |     path = malloc(end - pos + 1); | ||||||
|  |  | ||||||
|  |     if (path == NULL) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     *end = '\0'; |     *end = '\0'; | ||||||
|     return strcpy(path, pos); |     return strcpy(path, pos); | ||||||
| } | } | ||||||
| @@ -300,7 +315,12 @@ static int tmux_is_detached(void) | |||||||
|     session_info_stream = NULL; |     session_info_stream = NULL; | ||||||
|  |  | ||||||
|     /* prepare search string, for finding the current session's entry */ |     /* prepare search string, for finding the current session's entry */ | ||||||
|     search_str = (char *) malloc(numstr_len + 2); |     search_str = malloc(numstr_len + 2); | ||||||
|  |  | ||||||
|  |     if (search_str == NULL) { | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     search_str[0] = '\n'; |     search_str[0] = '\n'; | ||||||
|     strcpy(search_str + 1, mplex_data); |     strcpy(search_str + 1, mplex_data); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										464
									
								
								src/toxic.c
									
									
									
									
									
								
							
							
						
						
									
										464
									
								
								src/toxic.c
									
									
									
									
									
								
							| @@ -20,53 +20,53 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <ctype.h> | ||||||
| #include <curses.h> | #include <curses.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <stdio.h> | #include <getopt.h> | ||||||
| #include <stdlib.h> | #include <limits.h> | ||||||
|  | #include <locale.h> | ||||||
|  | #include <netdb.h> | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <stdarg.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdarg.h> | #include <stdio.h> | ||||||
| #include <signal.h> | #include <stdlib.h> | ||||||
| #include <locale.h> |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <time.h> | #include <sys/socket.h> | ||||||
| #include <pthread.h> |  | ||||||
| #include <getopt.h> |  | ||||||
| #include <netdb.h> |  | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <sys/socket.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <limits.h> |  | ||||||
| #include <termios.h> | #include <termios.h> | ||||||
| #include <ctype.h> | #include <time.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #include <curl/curl.h> | #include <curl/curl.h> | ||||||
| #include <tox/tox.h> |  | ||||||
| #include <tox/toxencryptsave.h> | #include <tox/toxencryptsave.h> | ||||||
|  | #include <tox/tox.h> | ||||||
|  |  | ||||||
|  | #include "audio_device.h" | ||||||
|  | #include "bootstrap.h" | ||||||
|  | #include "conference.h" | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
|  | #include "execute.h" | ||||||
|  | #include "file_transfers.h" | ||||||
|  | #include "friendlist.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "message_queue.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "name_lookup.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "prompt.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "term_mplex.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "friendlist.h" |  | ||||||
| #include "prompt.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "groupchat.h" |  | ||||||
| #include "file_transfers.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "notify.h" |  | ||||||
| #include "audio_device.h" |  | ||||||
| #include "message_queue.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "term_mplex.h" |  | ||||||
| #include "name_lookup.h" |  | ||||||
| #include "bootstrap.h" |  | ||||||
|  |  | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
| #include "xtra.h" | #include "x11focus.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| @@ -103,7 +103,6 @@ struct av_thread av_thread; | |||||||
| struct arg_opts arg_opts; | struct arg_opts arg_opts; | ||||||
| struct user_settings *user_settings = NULL; | struct user_settings *user_settings = NULL; | ||||||
|  |  | ||||||
|  |  | ||||||
| static struct user_password { | static struct user_password { | ||||||
|     bool data_is_encrypted; |     bool data_is_encrypted; | ||||||
|     char pass[MAX_PASSWORD_LEN + 1]; |     char pass[MAX_PASSWORD_LEN + 1]; | ||||||
| @@ -129,12 +128,12 @@ static void catch_SIGSEGV(int sig) | |||||||
| { | { | ||||||
|     UNUSED_VAR(sig); |     UNUSED_VAR(sig); | ||||||
|  |  | ||||||
|     if (!freopen("/dev/tty", "w", stderr)) {    // make sure stderr is enabled since we may have disabled it |     if (freopen("/dev/tty", "w", stderr)) {    // make sure stderr is enabled since we may have disabled it | ||||||
|         fprintf(stderr, "Warning: Failed to enable stderr\n"); |         fprintf(stderr, "Caught SIGSEGV: Aborting toxic session.\n"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     endwin(); |     endwin(); | ||||||
|     fprintf(stderr, "Caught SIGSEGV: Aborting toxic session.\n"); |  | ||||||
|     exit(EXIT_FAILURE); |     exit(EXIT_FAILURE); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -173,7 +172,10 @@ void free_global_data(void) | |||||||
| void exit_toxic_success(Tox *m) | void exit_toxic_success(Tox *m) | ||||||
| { | { | ||||||
|     store_data(m, DATA_FILE); |     store_data(m, DATA_FILE); | ||||||
|     memset(&user_password, 0, sizeof(struct user_password)); |  | ||||||
|  |     user_password = (struct user_password) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     terminate_notify(); |     terminate_notify(); | ||||||
|  |  | ||||||
| @@ -203,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); | ||||||
| @@ -216,12 +215,11 @@ void exit_toxic_err(const char *errmsg, int errcode) | |||||||
| { | { | ||||||
|     free_global_data(); |     free_global_data(); | ||||||
|  |  | ||||||
|     if (!freopen("/dev/tty", "w", stderr)) { |     if (freopen("/dev/tty", "w", stderr)) { | ||||||
|         fprintf(stderr, "Warning: Failed to open stderr\n"); |         fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     endwin(); |     endwin(); | ||||||
|     fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg); |  | ||||||
|     exit(EXIT_FAILURE); |     exit(EXIT_FAILURE); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -258,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) { | ||||||
| @@ -270,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(); | ||||||
| @@ -363,33 +470,39 @@ static void load_conferences(Tox *m) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint32_t chatlist[num_chats]; |     uint32_t *chatlist = malloc(num_chats * sizeof(uint32_t)); | ||||||
|  |  | ||||||
|  |     if (chatlist == NULL) { | ||||||
|  |         fprintf(stderr, "malloc() failed in load_conferences()\n"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     tox_conference_get_chatlist(m, chatlist); |     tox_conference_get_chatlist(m, chatlist); | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < num_chats; ++i) { |     for (size_t i = 0; i < num_chats; ++i) { | ||||||
|         uint32_t groupnum = chatlist[i]; |         uint32_t conferencenum = chatlist[i]; | ||||||
|  |  | ||||||
|         if (get_num_active_windows() >= MAX_WINDOWS_NUM) { |         if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||||
|             tox_conference_delete(m, groupnum, NULL); |             tox_conference_delete(m, conferencenum, NULL); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Tox_Err_Conference_Get_Type err; |         Tox_Err_Conference_Get_Type err; | ||||||
|         Tox_Conference_Type type = tox_conference_get_type(m, groupnum, &err); |         Tox_Conference_Type type = tox_conference_get_type(m, conferencenum, &err); | ||||||
|  |  | ||||||
|         if (err != TOX_ERR_CONFERENCE_GET_TYPE_OK) { |         if (err != TOX_ERR_CONFERENCE_GET_TYPE_OK) { | ||||||
|             tox_conference_delete(m, groupnum, NULL); |             tox_conference_delete(m, conferencenum, NULL); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Tox_Err_Conference_Title t_err; |         Tox_Err_Conference_Title t_err; | ||||||
|         size_t length = tox_conference_get_title_size(m, groupnum, &t_err); |         size_t length = tox_conference_get_title_size(m, conferencenum, &t_err); | ||||||
|         uint8_t title[MAX_STR_SIZE]; |         uint8_t title[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|         if (t_err != TOX_ERR_CONFERENCE_TITLE_OK || length >= sizeof(title)) { |         if (t_err != TOX_ERR_CONFERENCE_TITLE_OK || length >= sizeof(title)) { | ||||||
|             length = 0; |             length = 0; | ||||||
|         } else { |         } else { | ||||||
|             tox_conference_get_title(m, groupnum, title, &t_err); |             tox_conference_get_title(m, conferencenum, title, &t_err); | ||||||
|  |  | ||||||
|             if (t_err != TOX_ERR_CONFERENCE_TITLE_OK) { |             if (t_err != TOX_ERR_CONFERENCE_TITLE_OK) { | ||||||
|                 length = 0; |                 length = 0; | ||||||
| @@ -398,11 +511,25 @@ static void load_conferences(Tox *m) | |||||||
|  |  | ||||||
|         title[length] = 0; |         title[length] = 0; | ||||||
|  |  | ||||||
|         if (init_groupchat_win(m, groupnum, type, (const char *) title, length) == -1) { |         int win_idx = init_conference_win(m, conferencenum, type, (const char *) title, length); | ||||||
|             tox_conference_delete(m, groupnum, NULL); |  | ||||||
|  |         if (win_idx == -1) { | ||||||
|  |             tox_conference_delete(m, conferencenum, NULL); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
|  |             line_info_add(get_window_ptr(win_idx), NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|  | #ifdef AUDIO | ||||||
|  |                           "Use \"/audio on\" to enable audio in this conference." | ||||||
|  | #else | ||||||
|  |                           "Audio support disabled by compile-time option." | ||||||
|  | #endif | ||||||
|  |                          ); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     free(chatlist); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* return length of password on success, 0 on failure */ | /* return length of password on success, 0 on failure */ | ||||||
| @@ -422,12 +549,17 @@ static int password_prompt(char *buf, int size) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *p = fgets(buf, size, stdin); |     const char *p = fgets(buf, size, stdin); | ||||||
|     int len = strlen(buf); |  | ||||||
|  |  | ||||||
|     /* re-enable terminal echo */ |     /* re-enable terminal echo */ | ||||||
|     tcsetattr(fileno(stdin), TCSANOW, &oflags); |     tcsetattr(fileno(stdin), TCSANOW, &oflags); | ||||||
|  |  | ||||||
|     if (p == NULL || len <= 1) { |     if (p == NULL) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t len = strlen(buf); | ||||||
|  |  | ||||||
|  |     if (len <= 1) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -568,12 +700,19 @@ int store_data(Tox *m, const char *path) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char temp_path[strlen(path) + strlen(TEMP_PROFILE_EXT) + 1]; |     size_t temp_buf_size = strlen(path) + strlen(TEMP_PROFILE_EXT) + 1; | ||||||
|     snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_PROFILE_EXT); |     char *temp_path = malloc(temp_buf_size); | ||||||
|  |  | ||||||
|  |     if (temp_path == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(temp_path, temp_buf_size, "%s%s", path, TEMP_PROFILE_EXT); | ||||||
|  |  | ||||||
|     FILE *fp = fopen(temp_path, "wb"); |     FILE *fp = fopen(temp_path, "wb"); | ||||||
|  |  | ||||||
|     if (fp == NULL) { |     if (fp == NULL) { | ||||||
|  |         free(temp_path); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -581,6 +720,7 @@ int store_data(Tox *m, const char *path) | |||||||
|     char *data = malloc(data_len * sizeof(char)); |     char *data = malloc(data_len * sizeof(char)); | ||||||
|  |  | ||||||
|     if (data == NULL) { |     if (data == NULL) { | ||||||
|  |         free(temp_path); | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -593,6 +733,7 @@ int store_data(Tox *m, const char *path) | |||||||
|  |  | ||||||
|         if (enc_data == NULL) { |         if (enc_data == NULL) { | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(temp_path); | ||||||
|             free(data); |             free(data); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| @@ -604,6 +745,7 @@ int store_data(Tox *m, const char *path) | |||||||
|         if (err != TOX_ERR_ENCRYPTION_OK) { |         if (err != TOX_ERR_ENCRYPTION_OK) { | ||||||
|             fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err); |             fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err); | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(temp_path); | ||||||
|             free(data); |             free(data); | ||||||
|             free(enc_data); |             free(enc_data); | ||||||
|             return -1; |             return -1; | ||||||
| @@ -612,6 +754,7 @@ int store_data(Tox *m, const char *path) | |||||||
|         if (fwrite(enc_data, enc_len, 1, fp) != 1) { |         if (fwrite(enc_data, enc_len, 1, fp) != 1) { | ||||||
|             fprintf(stderr, "Failed to write profile data.\n"); |             fprintf(stderr, "Failed to write profile data.\n"); | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(temp_path); | ||||||
|             free(data); |             free(data); | ||||||
|             free(enc_data); |             free(enc_data); | ||||||
|             return -1; |             return -1; | ||||||
| @@ -622,6 +765,7 @@ int store_data(Tox *m, const char *path) | |||||||
|         if (fwrite(data, data_len, 1, fp) != 1) { |         if (fwrite(data, data_len, 1, fp) != 1) { | ||||||
|             fprintf(stderr, "Failed to write profile data.\n"); |             fprintf(stderr, "Failed to write profile data.\n"); | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(temp_path); | ||||||
|             free(data); |             free(data); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| @@ -631,9 +775,12 @@ int store_data(Tox *m, const char *path) | |||||||
|     free(data); |     free(data); | ||||||
|  |  | ||||||
|     if (rename(temp_path, path) != 0) { |     if (rename(temp_path, path) != 0) { | ||||||
|  |         free(temp_path); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     free(temp_path); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -722,10 +869,16 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New | |||||||
|             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); |             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         char data[len]; |         char *data = malloc(len); | ||||||
|  |  | ||||||
|         if (fread(data, sizeof(data), 1, fp) != 1) { |         if (data == NULL) { | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             exit_toxic_err("failed in load_tox", FATALERR_MEMORY); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (fread(data, len, 1, fp) != 1) { | ||||||
|  |             fclose(fp); | ||||||
|  |             free(data); | ||||||
|             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); |             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -734,6 +887,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New | |||||||
|         /* attempt to encrypt an already encrypted data file */ |         /* attempt to encrypt an already encrypted data file */ | ||||||
|         if (arg_opts.encrypt_data && is_encrypted) { |         if (arg_opts.encrypt_data && is_encrypted) { | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(data); | ||||||
|             exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT); |             exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -757,7 +911,13 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; |             size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; | ||||||
|             char plain[plain_len]; |             char *plain = malloc(plain_len);  // must be freed after tox_new() | ||||||
|  |  | ||||||
|  |             if (plain == NULL) { | ||||||
|  |                 fclose(fp); | ||||||
|  |                 free(data); | ||||||
|  |                 exit_toxic_err("failed in load_tox", FATALERR_MEMORY); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             while (true) { |             while (true) { | ||||||
|                 fflush(stdout); // Flush before prompts so the user sees the question/message |                 fflush(stdout); // Flush before prompts so the user sees the question/message | ||||||
| @@ -772,6 +932,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New | |||||||
|  |  | ||||||
|                 if (strcasecmp(user_password.pass, "q") == 0) { |                 if (strcasecmp(user_password.pass, "q") == 0) { | ||||||
|                     fclose(fp); |                     fclose(fp); | ||||||
|  |                     free(plain); | ||||||
|  |                     free(data); | ||||||
|                     exit(0); |                     exit(0); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -795,6 +957,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New | |||||||
|  |  | ||||||
|                     if (m == NULL) { |                     if (m == NULL) { | ||||||
|                         fclose(fp); |                         fclose(fp); | ||||||
|  |                         free(data); | ||||||
|  |                         free(plain); | ||||||
|                         return NULL; |                         return NULL; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
| @@ -806,9 +970,13 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New | |||||||
|                     pweval = 0; |                     pweval = 0; | ||||||
|                 } else { |                 } else { | ||||||
|                     fclose(fp); |                     fclose(fp); | ||||||
|  |                     free(data); | ||||||
|  |                     free(plain); | ||||||
|                     exit_toxic_err("tox_pass_decrypt() failed", pwerr); |                     exit_toxic_err("tox_pass_decrypt() failed", pwerr); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             free(plain); | ||||||
|         } else {   /* data is not encrypted */ |         } else {   /* data is not encrypted */ | ||||||
|             tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE); |             tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE); | ||||||
|             tox_options_set_savedata_data(tox_opts, (uint8_t *) data, len); |             tox_options_set_savedata_data(tox_opts, (uint8_t *) data, len); | ||||||
| @@ -817,11 +985,13 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New | |||||||
|  |  | ||||||
|             if (m == NULL) { |             if (m == NULL) { | ||||||
|                 fclose(fp); |                 fclose(fp); | ||||||
|  |                 free(data); | ||||||
|                 return NULL; |                 return NULL; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|  |         free(data); | ||||||
|     } else {   /* Data file does not/should not exist */ |     } else {   /* Data file does not/should not exist */ | ||||||
|         if (file_exists(data_path)) { |         if (file_exists(data_path)) { | ||||||
|             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); |             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); | ||||||
| @@ -894,6 +1064,7 @@ static void do_toxic(Tox *m) | |||||||
|  |  | ||||||
|     tox_iterate(m, NULL); |     tox_iterate(m, NULL); | ||||||
|     do_tox_connection(m); |     do_tox_connection(m); | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -932,20 +1103,18 @@ void *thread_cqueue(void *data) | |||||||
|     while (true) { |     while (true) { | ||||||
|         pthread_mutex_lock(&Winthread.lock); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|         size_t i; |         for (size_t i = 2; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |  | ||||||
|         for (i = 2; i < MAX_WINDOWS_NUM; ++i) { |  | ||||||
|             ToxWindow *toxwin = get_window_ptr(i); |             ToxWindow *toxwin = get_window_ptr(i); | ||||||
|  |  | ||||||
|             if (toxwin != NULL && toxwin->is_chat |             if ((toxwin != NULL) && (toxwin->type == WINDOW_TYPE_CHAT) | ||||||
|                     && get_friend_connection_status(toxwin->num) != TOX_CONNECTION_NONE) { |                     && (get_friend_connection_status(toxwin->num) != TOX_CONNECTION_NONE)) { | ||||||
|                 cqueue_try_send(toxwin, m); |                 cqueue_try_send(toxwin, m); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|         usleep(4000); |         sleep_thread(750000L); // 0.75 seconds | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -959,7 +1128,8 @@ void *thread_av(void *data) | |||||||
|         toxav_iterate(av); |         toxav_iterate(av); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|         usleep(toxav_iteration_interval(av) * 1000); |         long int sleep_duration = toxav_iteration_interval(av) * 1000; | ||||||
|  |         sleep_thread(sleep_duration); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| @@ -995,7 +1165,9 @@ static void print_version(void) | |||||||
|  |  | ||||||
| static void set_default_opts(void) | static void set_default_opts(void) | ||||||
| { | { | ||||||
|     memset(&arg_opts, 0, sizeof(struct arg_opts)); |     arg_opts = (struct arg_opts) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     /* set any non-zero defaults here*/ |     /* set any non-zero defaults here*/ | ||||||
|     arg_opts.proxy_type = TOX_PROXY_TYPE_NONE; |     arg_opts.proxy_type = TOX_PROXY_TYPE_NONE; | ||||||
| @@ -1043,6 +1215,11 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'c': |             case 'c': | ||||||
|  |                 if (optarg == NULL) { | ||||||
|  |                     queue_init_message("Invalid argument for option: %d", opt); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg); |                 snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg); | ||||||
|  |  | ||||||
|                 if (!file_exists(arg_opts.config_path)) { |                 if (!file_exists(arg_opts.config_path)) { | ||||||
| @@ -1061,23 +1238,31 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'f': |             case 'f': | ||||||
|  |                 if (optarg == NULL) { | ||||||
|  |                     queue_init_message("Invalid argument for option: %d", opt); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 arg_opts.use_custom_data = 1; |                 arg_opts.use_custom_data = 1; | ||||||
|  |  | ||||||
|                 if (DATA_FILE) { |                 if (DATA_FILE) { | ||||||
|                     free(DATA_FILE); |                     free(DATA_FILE); | ||||||
|  |                     DATA_FILE = NULL; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (BLOCK_FILE) { |                 if (BLOCK_FILE) { | ||||||
|                     free(BLOCK_FILE); |                     free(BLOCK_FILE); | ||||||
|  |                     BLOCK_FILE = NULL; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 DATA_FILE = malloc(strlen(optarg) + 1); |                 DATA_FILE = malloc(strlen(optarg) + 1); | ||||||
|                 strcpy(DATA_FILE, optarg); |  | ||||||
|  |  | ||||||
|                 if (DATA_FILE == NULL) { |                 if (DATA_FILE == NULL) { | ||||||
|                     exit_toxic_err("failed in parse_args", FATALERR_MEMORY); |                     exit_toxic_err("failed in parse_args", FATALERR_MEMORY); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 strcpy(DATA_FILE, optarg); | ||||||
|  |  | ||||||
|                 BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1); |                 BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1); | ||||||
|  |  | ||||||
|                 if (BLOCK_FILE == NULL) { |                 if (BLOCK_FILE == NULL) { | ||||||
| @@ -1118,6 +1303,11 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'n': |             case 'n': | ||||||
|  |                 if (optarg == NULL) { | ||||||
|  |                     queue_init_message("Invalid argument for option: %d", opt); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg); |                 snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| @@ -1127,6 +1317,11 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'p': |             case 'p': | ||||||
|  |                 if (optarg == NULL) { | ||||||
|  |                     queue_init_message("Invalid argument for option: %d", opt); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5; |                 arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5; | ||||||
|                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); |                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); | ||||||
|  |  | ||||||
| @@ -1144,6 +1339,11 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'P': |             case 'P': | ||||||
|  |                 if (optarg == NULL) { | ||||||
|  |                     queue_init_message("Invalid argument for option: %d", opt); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP; |                 arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP; | ||||||
|                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); |                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); | ||||||
|  |  | ||||||
| @@ -1161,6 +1361,11 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'r': |             case 'r': | ||||||
|  |                 if (optarg == NULL) { | ||||||
|  |                     queue_init_message("Invalid argument for option: %d", opt); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 snprintf(arg_opts.nameserver_path, sizeof(arg_opts.nameserver_path), "%s", optarg); |                 snprintf(arg_opts.nameserver_path, sizeof(arg_opts.nameserver_path), "%s", optarg); | ||||||
|  |  | ||||||
|                 if (!file_exists(arg_opts.nameserver_path)) { |                 if (!file_exists(arg_opts.nameserver_path)) { | ||||||
| @@ -1174,10 +1379,15 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'T': |             case 'T': | ||||||
|  |                 if (optarg == NULL) { | ||||||
|  |                     queue_init_message("Invalid argument for option: %d", opt); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 port = strtol(optarg, NULL, 10); |                 port = strtol(optarg, NULL, 10); | ||||||
|  |  | ||||||
|                 if (port <= 0 || port > MAX_PORT_RANGE) { |                 if (port <= 0 || port > MAX_PORT_RANGE) { | ||||||
|                     port = 14191; |                     port = MAX_PORT_RANGE; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 arg_opts.tcp_port = port; |                 arg_opts.tcp_port = port; | ||||||
| @@ -1192,6 +1402,8 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 exit(EXIT_SUCCESS); |                 exit(EXIT_SUCCESS); | ||||||
|  |  | ||||||
|             case 'h': |             case 'h': | ||||||
|  |  | ||||||
|  |             // Intentional fallthrough | ||||||
|             default: |             default: | ||||||
|                 print_usage(); |                 print_usage(); | ||||||
|                 exit(EXIT_SUCCESS); |                 exit(EXIT_SUCCESS); | ||||||
| @@ -1199,50 +1411,6 @@ static void parse_args(int argc, char *argv[]) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Looks for an old default profile data file and blocklist, and renames them to the new default names. |  | ||||||
|  * |  | ||||||
|  * Returns 0 on success. |  | ||||||
|  * Returns -1 on failure. |  | ||||||
|  */ |  | ||||||
| #define OLD_DATA_NAME "data" |  | ||||||
| #define OLD_DATA_BLOCKLIST_NAME "data-blocklist" |  | ||||||
| static int rename_old_profile(const char *user_config_dir) |  | ||||||
| { |  | ||||||
|     char old_data_file[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_NAME) + 1]; |  | ||||||
|     snprintf(old_data_file, sizeof(old_data_file), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_NAME); |  | ||||||
|  |  | ||||||
|     if (!file_exists(old_data_file)) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (file_exists(DATA_FILE)) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (rename(old_data_file, DATA_FILE) != 0) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     queue_init_message("Data file has been moved to %s", DATA_FILE); |  | ||||||
|  |  | ||||||
|     char old_data_blocklist[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_BLOCKLIST_NAME) + 1]; |  | ||||||
|     snprintf(old_data_blocklist, sizeof(old_data_blocklist), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_BLOCKLIST_NAME); |  | ||||||
|  |  | ||||||
|     if (!file_exists(old_data_blocklist)) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (file_exists(BLOCK_FILE)) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (rename(old_data_blocklist, BLOCK_FILE) != 0) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Initializes the default config directory and data files used by toxic. | /* Initializes the default config directory and data files used by toxic. | ||||||
|  * |  * | ||||||
|  * Exits the process with an error on failure. |  * Exits the process with an error on failure. | ||||||
| @@ -1285,29 +1453,9 @@ static void init_default_data_files(void) | |||||||
|         strcat(BLOCK_FILE, BLOCKNAME); |         strcat(BLOCK_FILE, BLOCKNAME); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* For backwards compatibility with old toxic profile names. TODO: remove this some day */ |  | ||||||
|     if (rename_old_profile(user_config_dir) == -1) { |  | ||||||
|         queue_init_message("Warning: Profile backwards compatibility failed."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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. */ | ||||||
| @@ -1365,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"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1382,21 +1530,12 @@ 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); | ||||||
|  |  | ||||||
|     /* thread for ncurses stuff */ |  | ||||||
|     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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) { |  | ||||||
|         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* thread for message queue */ |  | ||||||
|     if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) { |  | ||||||
|         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|     av = init_audio(prompt, m); |     av = init_audio(prompt, m); | ||||||
| @@ -1411,8 +1550,8 @@ int main(int argc, char **argv) | |||||||
|         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); |         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     set_primary_device(input, user_settings->audio_in_dev); |     set_al_device(input, user_settings->audio_in_dev); | ||||||
|     set_primary_device(output, user_settings->audio_out_dev); |     set_al_device(output, user_settings->audio_out_dev); | ||||||
|  |  | ||||||
| #elif SOUND_NOTIFY | #elif SOUND_NOTIFY | ||||||
|  |  | ||||||
| @@ -1422,6 +1561,16 @@ int main(int argc, char **argv) | |||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  |     /* thread for ncurses stuff */ | ||||||
|  |     if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) { | ||||||
|  |         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* thread for message queue */ | ||||||
|  |     if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) { | ||||||
|  |         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); | ||||||
|  |     } | ||||||
|  |  | ||||||
| #ifdef PYTHON | #ifdef PYTHON | ||||||
|  |  | ||||||
|     init_python(m); |     init_python(m); | ||||||
| @@ -1464,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); | ||||||
| @@ -1472,7 +1621,8 @@ int main(int argc, char **argv) | |||||||
|             last_save = cur_time; |             last_save = cur_time; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         usleep(tox_iteration_interval(m) * 1000); |         long int sleep_duration = tox_iteration_interval(m) * 1000; | ||||||
|  |         sleep_thread(sleep_duration); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								src/toxic.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/toxic.h
									
									
									
									
									
								
							| @@ -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 { | ||||||
| @@ -115,14 +116,14 @@ void on_friend_name(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t | |||||||
| void on_friend_status(Tox *m, uint32_t friendnumber, Tox_User_Status status, void *userdata); | void on_friend_status(Tox *m, uint32_t friendnumber, Tox_User_Status status, void *userdata); | ||||||
| void on_friend_status_message(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | void on_friend_status_message(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | ||||||
| void on_friend_added(Tox *m, uint32_t friendnumber, bool sort); | void on_friend_added(Tox *m, uint32_t friendnumber, bool sort); | ||||||
| void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type, | void on_conference_message(Tox *m, uint32_t conferencenumber, uint32_t peernumber, Tox_Message_Type type, | ||||||
|                            const uint8_t *message, size_t length, void *userdata); |                            const uint8_t *message, size_t length, void *userdata); | ||||||
| void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *group_pub_key, | void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *conference_pub_key, | ||||||
|                           size_t length, void *userdata); |                           size_t length, void *userdata); | ||||||
| void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata); | void on_conference_peer_list_changed(Tox *m, uint32_t conferencenumber, void *userdata); | ||||||
| void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name, | void on_conference_peer_name(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *name, | ||||||
|                              size_t length, void *userdata); |                              size_t length, void *userdata); | ||||||
| void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length, | void on_conference_title(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *title, size_t length, | ||||||
|                          void *userdata); |                          void *userdata); | ||||||
| void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, | void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, | ||||||
|                            void *userdata); |                            void *userdata); | ||||||
|   | |||||||
| @@ -24,11 +24,11 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <wchar.h> | #include <wchar.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "toxic_strings.h" |  | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| /* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */ | /* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */ | ||||||
| int add_char_to_buf(ChatContext *ctx, wint_t ch) | int add_char_to_buf(ChatContext *ctx, wint_t ch) | ||||||
|   | |||||||
							
								
								
									
										317
									
								
								src/video_call.c
									
									
									
									
									
								
							
							
						
						
									
										317
									
								
								src/video_call.c
									
									
									
									
									
								
							| @@ -20,27 +20,27 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "video_call.h" |  | ||||||
| #include "video_device.h" |  | ||||||
| #include "chat_commands.h" | #include "chat_commands.h" | ||||||
| #include "global_commands.h" | #include "global_commands.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "notify.h" |  | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "video_call.h" | ||||||
|  | #include "video_device.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <curses.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <pthread.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  | #include <curses.h> | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|  |  | ||||||
| #define default_video_bit_rate 5000 | #define DEFAULT_VIDEO_BIT_RATE 5000 | ||||||
|  |  | ||||||
| void on_video_receive_frame(ToxAV *av, uint32_t friend_number, | void on_video_receive_frame(ToxAV *av, uint32_t friend_number, | ||||||
|                             uint16_t width, uint16_t height, |                             uint16_t width, uint16_t height, | ||||||
| @@ -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) | ||||||
| @@ -62,17 +62,17 @@ ToxAV *init_video(ToxWindow *self, Tox *tox) | |||||||
|     CallControl.video_errors = ve_None; |     CallControl.video_errors = ve_None; | ||||||
|  |  | ||||||
|     CallControl.video_enabled = true; |     CallControl.video_enabled = true; | ||||||
|     CallControl.video_bit_rate = 0; |     CallControl.default_video_bit_rate = 0; | ||||||
|     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; | ||||||
|     } |     } | ||||||
| @@ -92,8 +92,9 @@ void terminate_video(void) | |||||||
|  |  | ||||||
|         stop_video_transmission(this_call, i); |         stop_video_transmission(this_call, i); | ||||||
|  |  | ||||||
|         if (this_call->vout_idx != -1) { |         if (this_call->status == cs_Active && this_call->vout_idx != -1) { | ||||||
|             close_video_device(vdt_output, this_call->vout_idx); |             close_video_device(vdt_output, this_call->vout_idx); | ||||||
|  |             this_call->vout_idx = -1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -108,18 +109,18 @@ void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y, | |||||||
|     Toxav_Err_Send_Frame error; |     Toxav_Err_Send_Frame error; | ||||||
|  |  | ||||||
|     /* Drop frame if video sending is disabled */ |     /* Drop frame if video sending is disabled */ | ||||||
|     if (CallControl.video_bit_rate == 0 || 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"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -137,24 +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 transmission"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video transmission"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     CallControl.video_bit_rate = default_video_bit_rate; |     if (open_primary_video_device(vdt_input, &call->vin_idx, &call->video_width, &call->video_height) != vde_None) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!"); | ||||||
|     if (toxav_video_set_bit_rate(CallControl.av, self->num, CallControl.video_bit_rate, NULL) == false) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (open_primary_video_device(vdt_input, &call->vin_idx) != vde_None) { |  | ||||||
|         line_info_add(self, NULL, 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; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!toxav_video_set_bit_rate(CallControl.av, self->num, call->video_bit_rate, NULL)) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -163,8 +162,12 @@ int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call) | |||||||
|  |  | ||||||
| int stop_video_transmission(Call *call, int friend_number) | int stop_video_transmission(Call *call, int friend_number) | ||||||
| { | { | ||||||
|     CallControl.video_bit_rate = 0; |     if (call->status != cs_Active) { | ||||||
|     toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL); |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     call->video_bit_rate = 0; | ||||||
|  |     toxav_video_set_bit_rate(CallControl.av, friend_number, call->video_bit_rate, NULL); | ||||||
|  |  | ||||||
|     if (call->vin_idx != -1) { |     if (call->vin_idx != -1) { | ||||||
|         close_video_device(vdt_input, call->vin_idx); |         close_video_device(vdt_input, call->vin_idx); | ||||||
| @@ -200,24 +203,33 @@ void on_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rat | |||||||
|     UNUSED_VAR(av); |     UNUSED_VAR(av); | ||||||
|     UNUSED_VAR(user_data); |     UNUSED_VAR(user_data); | ||||||
|  |  | ||||||
|     CallControl.video_bit_rate = video_bit_rate; |     Call *call = &CallControl.calls[friend_number]; | ||||||
|     toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL); |     call->video_bit_rate = video_bit_rate; | ||||||
|  |  | ||||||
|  |     /* TODO: with current toxav using one-pass VP8, the value of | ||||||
|  |      * video_bit_rate has no effect, except to disable video if it is 0. | ||||||
|  |      * Automatically change resolution instead? */ | ||||||
|  |     toxav_video_set_bit_rate(CallControl.av, friend_number, call->video_bit_rate, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| void callback_recv_video_starting(uint32_t friend_number) | void callback_recv_video_starting(uint32_t friend_number) | ||||||
| { | { | ||||||
|     Call *this_call = &CallControl.calls[friend_number]; |     Call *this_call = &CallControl.calls[friend_number]; | ||||||
|  |  | ||||||
|     if (this_call->vout_idx != -1) { |     if (this_call->status != cs_Active || this_call->vout_idx != -1) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     open_primary_video_device(vdt_output, &this_call->vout_idx); |     open_primary_video_device(vdt_output, &this_call->vout_idx, NULL, NULL); | ||||||
| } | } | ||||||
| void callback_recv_video_end(uint32_t friend_number) | void callback_recv_video_end(uint32_t friend_number) | ||||||
| { | { | ||||||
|     Call *this_call = &CallControl.calls[friend_number]; |     Call *this_call = &CallControl.calls[friend_number]; | ||||||
|  |  | ||||||
|  |     if (this_call->status != cs_Active || this_call->vout_idx == -1) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     close_video_device(vdt_output, this_call->vout_idx); |     close_video_device(vdt_output, this_call->vout_idx); | ||||||
|     this_call->vout_idx = -1; |     this_call->vout_idx = -1; | ||||||
| } | } | ||||||
| @@ -235,12 +247,9 @@ void callback_video_starting(uint32_t friend_number) | |||||||
|             ToxWindow *window = get_window_ptr(i); |             ToxWindow *window = get_window_ptr(i); | ||||||
|  |  | ||||||
|             if (window != NULL && window->is_call && window->num == friend_number) { |             if (window != NULL && window->is_call && window->num == friend_number) { | ||||||
|                 if (0 != start_video_transmission(window, CallControl.av, this_call)) { |                 if (start_video_transmission(window, CallControl.av, this_call) == 0) { | ||||||
|                     line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!"); |                     line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting."); | ||||||
|                     return; |  | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting."); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -258,44 +267,122 @@ void callback_video_end(uint32_t friend_number) | |||||||
| /* | /* | ||||||
|  * Commands from chat_commands.h |  * Commands from chat_commands.h | ||||||
|  */ |  */ | ||||||
|  | void cmd_vcall(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |     UNUSED_VAR(m); | ||||||
|  |     UNUSED_VAR(argv); | ||||||
|  |  | ||||||
|  |     if (argc != 0) { | ||||||
|  |         print_err(self, "Unknown arguments."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!CallControl.av) { | ||||||
|  |         print_err(self, "ToxAV not supported!"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!self->stb->connection) { | ||||||
|  |         print_err(self, "Friend is offline."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Call *call = &CallControl.calls[self->num]; | ||||||
|  |  | ||||||
|  |     if (call->status != cs_None) { | ||||||
|  |         print_err(self, "Already calling."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     init_call(call); | ||||||
|  |  | ||||||
|  |     call->video_bit_rate = DEFAULT_VIDEO_BIT_RATE; | ||||||
|  |  | ||||||
|  |     place_call(self); | ||||||
|  | } | ||||||
|  |  | ||||||
| void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|     UNUSED_VAR(m); |     UNUSED_VAR(m); | ||||||
|     UNUSED_VAR(argv); |     UNUSED_VAR(argv); | ||||||
|  |  | ||||||
|     const char *error_str; |  | ||||||
|     Call *this_call = &CallControl.calls[self->num]; |     Call *this_call = &CallControl.calls[self->num]; | ||||||
|  |  | ||||||
|     if (argc != 0) { |     if (argc != 0) { | ||||||
|         error_str = "Unknown arguments."; |         print_err(self, "Unknown arguments."); | ||||||
|         goto on_error; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!CallControl.av) { |     if (!CallControl.av) { | ||||||
|         error_str = "ToxAV not supported!"; |         print_err(self, "ToxAV not supported!"); | ||||||
|         goto on_error; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!self->stb->connection) { |     if (!self->stb->connection) { | ||||||
|         error_str = "Friend is offline."; |         print_err(self, "Friend is offline."); | ||||||
|         goto on_error; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!self->is_call) { |     if (this_call->status != cs_Active) { | ||||||
|         error_str = "Not in call!"; |         print_err(self, "Not in call!"); | ||||||
|         goto on_error; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (this_call->vin_idx == -1) { |     if (this_call->vin_idx == -1) { | ||||||
|  |         this_call->video_bit_rate = DEFAULT_VIDEO_BIT_RATE; | ||||||
|         callback_video_starting(self->num); |         callback_video_starting(self->num); | ||||||
|     } else { |     } else { | ||||||
|         callback_video_end(self->num); |         callback_video_end(self->num); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|     return; | void cmd_res(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| on_error: | { | ||||||
|     print_err(self, error_str); |     UNUSED_VAR(window); | ||||||
|  |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|  |     Call *call = &CallControl.calls[self->num]; | ||||||
|  |  | ||||||
|  |     if (argc == 0) { | ||||||
|  |         if (call->status == cs_Active && call->vin_idx != -1) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|  |                           "Resolution of current call: %u x %u", | ||||||
|  |                           call->video_width, call->video_height); | ||||||
|  |         } else { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|  |                           "Initial resolution for video calls: %u x %u", | ||||||
|  |                           CallControl.default_video_width, CallControl.default_video_height); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (argc != 2) { | ||||||
|  |         print_err(self, "Require 0 or 2 arguments."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char *endw, *endh; | ||||||
|  |     const long int width = strtol(argv[1], &endw, 10); | ||||||
|  |     const long int height = strtol(argv[2], &endh, 10); | ||||||
|  |  | ||||||
|  |     if (*endw || *endh || width < 0 || height < 0) { | ||||||
|  |         print_err(self, "Invalid input"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (call->status == cs_Active && call->vin_idx != -1) { | ||||||
|  |         stop_video_transmission(call, self->num); | ||||||
|  |         call->video_width = width; | ||||||
|  |         call->video_height = height; | ||||||
|  |         call->video_bit_rate = DEFAULT_VIDEO_BIT_RATE; | ||||||
|  |         start_video_transmission(self, CallControl.av, call); | ||||||
|  |     } else { | ||||||
|  |         CallControl.default_video_width = width; | ||||||
|  |         CallControl.default_video_height = height; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -303,16 +390,14 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|     UNUSED_VAR(m); |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|     const char *error_str; |  | ||||||
|  |  | ||||||
|     if (argc != 1) { |     if (argc != 1) { | ||||||
|         if (argc < 1) { |         if (argc < 1) { | ||||||
|             error_str = "Type must be specified!"; |             print_err(self, "Type must be specified!"); | ||||||
|         } else { |         } else { | ||||||
|             error_str = "Only one argument allowed!"; |             print_err(self, "Only one argument allowed!"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         goto on_error; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     VideoDeviceType type; |     VideoDeviceType type; | ||||||
| @@ -326,15 +411,11 @@ 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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     print_video_devices(self, type); |     print_video_devices(self, type); | ||||||
|  |  | ||||||
|     return; |  | ||||||
| on_error: |  | ||||||
|     print_err(self, error_str); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* This changes primary video device only */ | /* This changes primary video device only */ | ||||||
| @@ -343,18 +424,16 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|     UNUSED_VAR(m); |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|     const char *error_str; |  | ||||||
|  |  | ||||||
|     if (argc != 2) { |     if (argc != 2) { | ||||||
|         if (argc < 1) { |         if (argc < 1) { | ||||||
|             error_str = "Type must be specified!"; |             print_err(self, "Type must be specified!"); | ||||||
|         } else if (argc < 2) { |         } else if (argc < 2) { | ||||||
|             error_str = "Must have id!"; |             print_err(self, "Must have id!"); | ||||||
|         } else { |         } else { | ||||||
|             error_str = "Only two arguments allowed!"; |             print_err(self, "Only two arguments allowed!"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         goto on_error; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     VideoDeviceType type; |     VideoDeviceType type; | ||||||
| @@ -368,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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -377,98 +456,14 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, | |||||||
|     long int selection = strtol(argv[2], &end, 10); |     long int selection = strtol(argv[2], &end, 10); | ||||||
|  |  | ||||||
|     if (*end) { |     if (*end) { | ||||||
|         error_str = "Invalid input"; |         print_err(self, "Invalid input"); | ||||||
|         goto on_error; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (set_primary_video_device(type, selection) == vde_InvalidSelection) { |     if (set_primary_video_device(type, selection) == vde_InvalidSelection) { | ||||||
|         error_str = "Invalid selection!"; |         print_err(self, "Invalid selection!"); | ||||||
|         goto on_error; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return; |  | ||||||
| on_error: |  | ||||||
|     print_err(self, error_str); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) |  | ||||||
| { |  | ||||||
|     UNUSED_VAR(window); |  | ||||||
|     UNUSED_VAR(m); |  | ||||||
|  |  | ||||||
|     const char *error_str; |  | ||||||
|  |  | ||||||
|     if (argc != 2) { |  | ||||||
|         if (argc < 1) { |  | ||||||
|             error_str = "Type must be specified!"; |  | ||||||
|         } else if (argc < 2) { |  | ||||||
|             error_str = "Must have id!"; |  | ||||||
|         } else { |  | ||||||
|             error_str = "Only two arguments allowed!"; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         goto on_error; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     VideoDeviceType type; |  | ||||||
|  |  | ||||||
|     if (strcmp(argv[1], "in") == 0) { /* Input devices */ |  | ||||||
|         type = vdt_input; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     else if (strcmp(argv[1], "out") == 0) { /* Output devices */ |  | ||||||
|         type = vdt_output; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     else { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     char *end; |  | ||||||
|     long int selection = strtol(argv[2], &end, 10); |  | ||||||
|  |  | ||||||
|     if (*end) { |  | ||||||
|         error_str = "Invalid input"; |  | ||||||
|         goto on_error; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (video_selection_valid(type, selection) == vde_InvalidSelection) { |  | ||||||
|         error_str = "Invalid selection!"; |  | ||||||
|         goto on_error; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* If call is active, change device */ |  | ||||||
|     if (self->is_call) { |  | ||||||
|         Call *this_call = &CallControl.calls[self->num]; |  | ||||||
|  |  | ||||||
|         if (this_call->ttas) { |  | ||||||
|  |  | ||||||
|             if (type == vdt_output) { |  | ||||||
|             } else { |  | ||||||
|                 /* TODO: check for failure */ |  | ||||||
|                 close_video_device(vdt_input, this_call->vin_idx); |  | ||||||
|                 open_video_device(vdt_input, selection, &this_call->vin_idx); |  | ||||||
|                 register_video_device_callback(self->num, this_call->vin_idx, read_video_device_callback, &self->num); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     self->video_device_selection[type] = selection; |  | ||||||
|  |  | ||||||
|     return; |  | ||||||
| on_error: |  | ||||||
|     print_err(self, error_str); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void stop_video_stream(ToxWindow *self) |  | ||||||
| { |  | ||||||
|     Call *this_call = &CallControl.calls[self->num]; |  | ||||||
|  |  | ||||||
|     if (this_call && this_call->vin_idx != -1) { |  | ||||||
|         stop_video_transmission(this_call, self->num); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|   | |||||||
| @@ -34,7 +34,6 @@ ToxAV *init_video(ToxWindow *self, Tox *tox); | |||||||
| void terminate_video(void); | void terminate_video(void); | ||||||
| int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call); | int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call); | ||||||
| int stop_video_transmission(Call *call, int friend_number); | int stop_video_transmission(Call *call, int friend_number); | ||||||
| void stop_video_stream(ToxWindow *self); |  | ||||||
|  |  | ||||||
| void callback_recv_video_starting(uint32_t friend_number); | void callback_recv_video_starting(uint32_t friend_number); | ||||||
| void callback_recv_video_end(uint32_t friend_number); | void callback_recv_video_end(uint32_t friend_number); | ||||||
|   | |||||||
| @@ -20,8 +20,8 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "video_device.h" |  | ||||||
| #include "video_call.h" | #include "video_call.h" | ||||||
|  | #include "video_device.h" | ||||||
|  |  | ||||||
| #include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||||
|  |  | ||||||
| @@ -30,13 +30,13 @@ | |||||||
| #if defined(__OSX__) || defined(__APPLE__) | #if defined(__OSX__) || defined(__APPLE__) | ||||||
| #import "osx_video.h" | #import "osx_video.h" | ||||||
| #else | #else | ||||||
| #include <X11/Xlib.h> |  | ||||||
| #include <X11/Xutil.h> |  | ||||||
| #include <X11/Xos.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/stat.h> |  | ||||||
| #include <sys/mman.h> |  | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <X11/Xlib.h> | ||||||
|  | #include <X11/Xos.h> | ||||||
|  | #include <X11/Xutil.h> | ||||||
| #if defined(__OpenBSD__) || defined(__NetBSD__) | #if defined(__OpenBSD__) || defined(__NetBSD__) | ||||||
| #include <sys/videoio.h> | #include <sys/videoio.h> | ||||||
| #else | #else | ||||||
| @@ -45,16 +45,15 @@ | |||||||
| #endif /* __OSX__ || __APPLE__ */ | #endif /* __OSX__ || __APPLE__ */ | ||||||
|  |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "settings.h" |  | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
|  | #include "settings.h" | ||||||
|  |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
| #include <unistd.h> | #include <stdbool.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|  |  | ||||||
| @@ -181,7 +180,7 @@ static int xioctl(int fh, unsigned long request, void *arg) | |||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
| VideoDeviceError init_video_devices(ToxAV *av_) | VideoDeviceError init_video_devices(ToxAV *av_) | ||||||
| #else | #else | ||||||
| VideoDeviceError init_video_devices() | VideoDeviceError init_video_devices(void) | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
| { | { | ||||||
|     size[vdt_input] = 0; |     size[vdt_input] = 0; | ||||||
| @@ -258,7 +257,7 @@ VideoDeviceError terminate_video_devices(void) | |||||||
|     video_thread_running = false; |     video_thread_running = false; | ||||||
|     unlock; |     unlock; | ||||||
|  |  | ||||||
|     usleep(20000); |     sleep_thread(20000L); | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -315,9 +314,10 @@ VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selectio | |||||||
|     return vde_None; |     return vde_None; | ||||||
| } | } | ||||||
|  |  | ||||||
| VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx) | VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx, | ||||||
|  |         uint32_t *width, uint32_t *height) | ||||||
| { | { | ||||||
|     return open_video_device(type, primary_video_device[type], device_idx); |     return open_video_device(type, primary_video_device[type], device_idx, width, height); | ||||||
| } | } | ||||||
|  |  | ||||||
| void get_primary_video_device_name(VideoDeviceType type, char *buf, int size) | void get_primary_video_device_name(VideoDeviceType type, char *buf, int size) | ||||||
| @@ -325,7 +325,8 @@ void get_primary_video_device_name(VideoDeviceType type, char *buf, int size) | |||||||
|     memcpy(buf, dvideo_device_names[type], size); |     memcpy(buf, dvideo_device_names[type], size); | ||||||
| } | } | ||||||
|  |  | ||||||
| VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx) | VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx, | ||||||
|  |                                    uint32_t *width, uint32_t *height) | ||||||
| { | { | ||||||
|     if (size[type] <= selection || selection < 0) { |     if (size[type] <= selection || selection < 0) { | ||||||
|         return vde_InvalidSelection; |         return vde_InvalidSelection; | ||||||
| @@ -372,6 +373,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint | |||||||
|  |  | ||||||
| #if defined(__OSX__) || defined(__APPLE__) | #if defined(__OSX__) || defined(__APPLE__) | ||||||
|  |  | ||||||
|  |         /* TODO: use requested resolution */ | ||||||
|         if (osx_video_open_device(selection, &device->video_width, &device->video_height) != 0) { |         if (osx_video_open_device(selection, &device->video_width, &device->video_height) != 0) { | ||||||
|             free(device); |             free(device); | ||||||
|             unlock; |             unlock; | ||||||
| @@ -401,11 +403,12 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* Setup video format */ |         /* Setup video format */ | ||||||
|         struct v4l2_format fmt; |         struct v4l2_format fmt = {0}; | ||||||
|         memset(&(fmt), 0, sizeof(fmt)); |  | ||||||
|  |  | ||||||
|         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|         fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; |         fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; | ||||||
|  |         fmt.fmt.pix.width = width == NULL ? 0 : *width; | ||||||
|  |         fmt.fmt.pix.height = height == NULL ? 0 : *height; | ||||||
|  |  | ||||||
|         if (-1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt)) { |         if (-1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt)) { | ||||||
|             close(device->fd); |             close(device->fd); | ||||||
| @@ -418,8 +421,8 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint | |||||||
|         device->video_height = fmt.fmt.pix.height; |         device->video_height = fmt.fmt.pix.height; | ||||||
|  |  | ||||||
|         /* Request buffers */ |         /* Request buffers */ | ||||||
|         struct v4l2_requestbuffers req; |         struct v4l2_requestbuffers req = {0}; | ||||||
|         memset(&(req), 0, sizeof(req)); |  | ||||||
|         req.count = 4; |         req.count = 4; | ||||||
|         req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |         req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|         req.memory = V4L2_MEMORY_MMAP; |         req.memory = V4L2_MEMORY_MMAP; | ||||||
| @@ -441,8 +444,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint | |||||||
|         device->buffers = calloc(req.count, sizeof(struct VideoBuffer)); |         device->buffers = calloc(req.count, sizeof(struct VideoBuffer)); | ||||||
|  |  | ||||||
|         for (i = 0; i < req.count; ++i) { |         for (i = 0; i < req.count; ++i) { | ||||||
|             struct v4l2_buffer buf; |             struct v4l2_buffer buf = {0}; | ||||||
|             memset(&(buf), 0, sizeof(buf)); |  | ||||||
|  |  | ||||||
|             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|             buf.memory = V4L2_MEMORY_MMAP; |             buf.memory = V4L2_MEMORY_MMAP; | ||||||
| @@ -479,8 +481,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint | |||||||
|         enum v4l2_buf_type type; |         enum v4l2_buf_type type; | ||||||
|  |  | ||||||
|         for (i = 0; i < device->n_buffers; ++i) { |         for (i = 0; i < device->n_buffers; ++i) { | ||||||
|             struct v4l2_buffer buf; |             struct v4l2_buffer buf = {0}; | ||||||
|             memset(&(buf), 0, sizeof(buf)); |  | ||||||
|  |  | ||||||
|             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|             buf.memory = V4L2_MEMORY_MMAP; |             buf.memory = V4L2_MEMORY_MMAP; | ||||||
| @@ -546,6 +547,14 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint | |||||||
|  |  | ||||||
|         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1); |         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1); | ||||||
|  |  | ||||||
|  |         if (width != NULL) { | ||||||
|  |             *width = device->video_width; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (height != NULL) { | ||||||
|  |             *height = device->video_height; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         video_thread_paused = false; |         video_thread_paused = false; | ||||||
|     } else { /* vdt_output */ |     } else { /* vdt_output */ | ||||||
|  |  | ||||||
| @@ -676,7 +685,7 @@ void *video_thread_poll(void *arg)  // TODO: maybe use thread for every input so | |||||||
|         unlock; |         unlock; | ||||||
|  |  | ||||||
|         if (video_thread_paused) { |         if (video_thread_paused) { | ||||||
|             usleep(10000);    /* Wait for unpause. */ |             sleep_thread(10000L);    /* Wait for unpause. */ | ||||||
|         } else { |         } else { | ||||||
|             for (i = 0; i < size[vdt_input]; ++i) { |             for (i = 0; i < size[vdt_input]; ++i) { | ||||||
|                 lock; |                 lock; | ||||||
| @@ -698,8 +707,7 @@ void *video_thread_poll(void *arg)  // TODO: maybe use thread for every input so | |||||||
|                     } |                     } | ||||||
|  |  | ||||||
| #else /* not __OSX__ || __APPLE__ */ | #else /* not __OSX__ || __APPLE__ */ | ||||||
|                     struct v4l2_buffer buf; |                     struct v4l2_buffer buf = {0}; | ||||||
|                     memset(&(buf), 0, sizeof(buf)); |  | ||||||
|  |  | ||||||
|                     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |                     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|                     buf.memory = V4L2_MEMORY_MMAP; |                     buf.memory = V4L2_MEMORY_MMAP; | ||||||
| @@ -765,7 +773,8 @@ void *video_thread_poll(void *arg)  // TODO: maybe use thread for every input so | |||||||
|                 unlock; |                 unlock; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             usleep(1000 * 1000 / 24); |             long int sleep_duration = 1000 * 1000 / 24; | ||||||
|  |             sleep_thread(sleep_duration); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -843,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; | ||||||
|   | |||||||
| @@ -62,9 +62,11 @@ VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t devic | |||||||
| void *get_video_device_callback_data(uint32_t device_idx); | void *get_video_device_callback_data(uint32_t device_idx); | ||||||
|  |  | ||||||
| VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection); | VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection); | ||||||
| VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx); | VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx, | ||||||
|  |         uint32_t *width, uint32_t *height); | ||||||
| /* Start device */ | /* Start device */ | ||||||
| VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx); | VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx, | ||||||
|  |                                    uint32_t *width, uint32_t *height); | ||||||
| /* Stop device */ | /* Stop device */ | ||||||
| VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx); | VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										246
									
								
								src/windows.c
									
									
									
									
									
								
							
							
						
						
									
										246
									
								
								src/windows.c
									
									
									
									
									
								
							| @@ -20,22 +20,22 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <pthread.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <pthread.h> |  | ||||||
| #include <ctype.h> |  | ||||||
|  |  | ||||||
| #include "friendlist.h" | #include "avatars.h" | ||||||
| #include "prompt.h" |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "groupchat.h" |  | ||||||
| #include "chat.h" | #include "chat.h" | ||||||
|  | #include "conference.h" | ||||||
|  | #include "file_transfers.h" | ||||||
|  | #include "friendlist.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "avatars.h" | #include "prompt.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "file_transfers.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern char *DATA_FILE; | extern char *DATA_FILE; | ||||||
| extern struct Winthread Winthread; | extern struct Winthread Winthread; | ||||||
| @@ -66,6 +66,8 @@ void on_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection c | |||||||
| { | { | ||||||
|     UNUSED_VAR(userdata); |     UNUSED_VAR(userdata); | ||||||
|  |  | ||||||
|  |     on_avatar_friend_connection_status(m, friendnumber, connection_status); | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] != NULL && windows[i]->onConnectionChange != NULL) { |         if (windows[i] != NULL && windows[i]->onConnectionChange != NULL) { | ||||||
|             windows[i]->onConnectionChange(windows[i], m, friendnumber, connection_status); |             windows[i]->onConnectionChange(windows[i], m, friendnumber, connection_status); | ||||||
| @@ -158,7 +160,7 @@ void on_friend_added(Tox *m, uint32_t friendnumber, bool sort) | |||||||
|     store_data(m, DATA_FILE); |     store_data(m, DATA_FILE); | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type, | void on_conference_message(Tox *m, uint32_t conferencenumber, uint32_t peernumber, Tox_Message_Type type, | ||||||
|                            const uint8_t *message, size_t length, void *userdata) |                            const uint8_t *message, size_t length, void *userdata) | ||||||
| { | { | ||||||
|     UNUSED_VAR(userdata); |     UNUSED_VAR(userdata); | ||||||
| @@ -167,36 +169,36 @@ void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, To | |||||||
|     length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); |     length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] != NULL && windows[i]->onGroupMessage != NULL) { |         if (windows[i] != NULL && windows[i]->onConferenceMessage != NULL) { | ||||||
|             windows[i]->onGroupMessage(windows[i], m, groupnumber, peernumber, type, msg, length); |             windows[i]->onConferenceMessage(windows[i], m, conferencenumber, peernumber, type, msg, length); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *group_pub_key, | void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *conference_pub_key, | ||||||
|                           size_t length, void *userdata) |                           size_t length, void *userdata) | ||||||
| { | { | ||||||
|     UNUSED_VAR(userdata); |     UNUSED_VAR(userdata); | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] != NULL && windows[i]->onGroupInvite != NULL) { |         if (windows[i] != NULL && windows[i]->onConferenceInvite != NULL) { | ||||||
|             windows[i]->onGroupInvite(windows[i], m, friendnumber, type, (char *) group_pub_key, length); |             windows[i]->onConferenceInvite(windows[i], m, friendnumber, type, (char *) conference_pub_key, length); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata) | void on_conference_peer_list_changed(Tox *m, uint32_t conferencenumber, void *userdata) | ||||||
| { | { | ||||||
|     UNUSED_VAR(userdata); |     UNUSED_VAR(userdata); | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] != NULL && windows[i]->onGroupNameListChange != NULL) { |         if (windows[i] != NULL && windows[i]->onConferenceNameListChange != NULL) { | ||||||
|             windows[i]->onGroupNameListChange(windows[i], m, groupnumber); |             windows[i]->onConferenceNameListChange(windows[i], m, conferencenumber); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name, | void on_conference_peer_name(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *name, | ||||||
|                              size_t length, void *userdata) |                              size_t length, void *userdata) | ||||||
| { | { | ||||||
|     UNUSED_VAR(userdata); |     UNUSED_VAR(userdata); | ||||||
| @@ -206,13 +208,13 @@ void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, | |||||||
|     filter_str(nick, length); |     filter_str(nick, length); | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] != NULL && windows[i]->onGroupPeerNameChange != NULL) { |         if (windows[i] != NULL && windows[i]->onConferencePeerNameChange != NULL) { | ||||||
|             windows[i]->onGroupPeerNameChange(windows[i], m, groupnumber, peernumber, nick, length); |             windows[i]->onConferencePeerNameChange(windows[i], m, conferencenumber, peernumber, nick, length); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length, | void on_conference_title(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *title, size_t length, | ||||||
|                          void *userdata) |                          void *userdata) | ||||||
| { | { | ||||||
|     UNUSED_VAR(userdata); |     UNUSED_VAR(userdata); | ||||||
| @@ -221,8 +223,8 @@ void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, cons | |||||||
|     length = copy_tox_str(data, sizeof(data), (const char *) title, length); |     length = copy_tox_str(data, sizeof(data), (const char *) title, length); | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] != NULL && windows[i]->onGroupTitleChange != NULL) { |         if (windows[i] != NULL && windows[i]->onConferenceTitleChange != NULL) { | ||||||
|             windows[i]->onGroupTitleChange(windows[i], m, groupnumber, peernumber, data, length); |             windows[i]->onConferenceTitleChange(windows[i], m, conferencenumber, peernumber, data, length); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -334,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; | ||||||
| @@ -395,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(); | ||||||
| @@ -406,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) { | ||||||
| @@ -428,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]->is_friendlist)  { |  | ||||||
|             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; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -454,7 +455,7 @@ void on_window_resize(void) | |||||||
|             wclear(w->help->win); |             wclear(w->help->win); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (w->is_groupchat) { |         if (w->type == WINDOW_TYPE_CONFERENCE) { | ||||||
|             delwin(w->chatwin->sidebar); |             delwin(w->chatwin->sidebar); | ||||||
|             w->chatwin->sidebar = NULL; |             w->chatwin->sidebar = NULL; | ||||||
|         } else { |         } else { | ||||||
| @@ -463,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->is_groupchat) { |             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) { | ||||||
| @@ -489,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]); |  | ||||||
|  |  | ||||||
|         if (i == active_window_index) { |  | ||||||
|  |  | ||||||
| #ifdef URXVT_FIX |  | ||||||
|             attroff(A_BOLD | COLOR_PAIR(GREEN)); |  | ||||||
|         } else { |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|             attroff(A_BOLD); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // restore cursor position after drawing |     int cur_x; | ||||||
|     move(y, x); |     int cur_y; | ||||||
|  |     UNUSED_VAR(cur_y); | ||||||
|  |  | ||||||
|     refresh(); |     getyx(win, cur_y, cur_x); | ||||||
|  |  | ||||||
|  |     wattron(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |     mvwhline(win, 0, cur_x, ' ', COLS - cur_x); | ||||||
|  |     wattroff(win, COLOR_PAIR(BAR_TEXT)); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -664,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); | ||||||
| @@ -682,7 +716,7 @@ void draw_active_window(Tox *m) | |||||||
|     if (printable == 0 && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) { |     if (printable == 0 && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) { | ||||||
|         set_next_window((int) ch); |         set_next_window((int) ch); | ||||||
|         return; |         return; | ||||||
|     } else if (printable == 0 && !a->is_friendlist) { |     } else if ((printable == 0) && (a->type != WINDOW_TYPE_FRIEND_LIST)) { | ||||||
|         pthread_mutex_lock(&Winthread.lock); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         bool input_ret = a->onKey(a, m, ch, (bool) printable); |         bool input_ret = a->onKey(a, m, ch, (bool) printable); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
| @@ -716,7 +750,7 @@ void refresh_inactive_windows(void) | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (i != active_window_index && !toxwin->is_friendlist) { |         if ((i != active_window_index) && (toxwin->type != WINDOW_TYPE_FRIEND_LIST)) { | ||||||
|             pthread_mutex_lock(&Winthread.lock); |             pthread_mutex_lock(&Winthread.lock); | ||||||
|             line_info_print(toxwin); |             line_info_print(toxwin); | ||||||
|             pthread_mutex_unlock(&Winthread.lock); |             pthread_mutex_unlock(&Winthread.lock); | ||||||
| @@ -754,7 +788,7 @@ int get_num_active_windows(void) | |||||||
|     return num_active_windows; |     return num_active_windows; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* destroys all chat and groupchat windows (should only be called on shutdown) */ | /* destroys all chat and conference windows (should only be called on shutdown) */ | ||||||
| void kill_all_windows(Tox *m) | void kill_all_windows(Tox *m) | ||||||
| { | { | ||||||
|     for (uint8_t i = 2; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 2; i < MAX_WINDOWS_NUM; ++i) { | ||||||
| @@ -762,10 +796,10 @@ void kill_all_windows(Tox *m) | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (windows[i]->is_chat) { |         if (windows[i]->type == WINDOW_TYPE_CHAT) { | ||||||
|             kill_chat_window(windows[i], m); |             kill_chat_window(windows[i], m); | ||||||
|         } else if (windows[i]->is_groupchat) { |         } else if (windows[i]->type == WINDOW_TYPE_CONFERENCE) { | ||||||
|             free_groupchat(windows[i], windows[i]->num); |             free_conference(windows[i], windows[i]->num); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,9 +24,9 @@ | |||||||
| #define WINDOWS_H | #define WINDOWS_H | ||||||
|  |  | ||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
| #include <wctype.h> |  | ||||||
| #include <wchar.h> |  | ||||||
| #include <signal.h> | #include <signal.h> | ||||||
|  | #include <wchar.h> | ||||||
|  | #include <wctype.h> | ||||||
|  |  | ||||||
| #include <tox/tox.h> | #include <tox/tox.h> | ||||||
|  |  | ||||||
| @@ -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,16 +53,33 @@ 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 { | ||||||
|  |     WINDOW_TYPE_PROMPT, | ||||||
|  |     WINDOW_TYPE_CHAT, | ||||||
|  |     WINDOW_TYPE_CONFERENCE, | ||||||
|  |     WINDOW_TYPE_FRIEND_LIST, | ||||||
|  | } WINDOW_TYPE; | ||||||
|  |  | ||||||
| /* Fixes text color problem on some terminals. | /* Fixes text color problem on some terminals. | ||||||
|    Uncomment if necessary */ |    Uncomment if necessary */ | ||||||
| /* #define URXVT_FIX */ | /* #define URXVT_FIX */ | ||||||
| @@ -125,11 +144,11 @@ struct ToxWindow { | |||||||
|     void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t); |     void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t); | ||||||
|     void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, Tox_User_Status); |     void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, Tox_User_Status); | ||||||
|     void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t); |     void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t); | ||||||
|     void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_Message_Type, const char *, size_t); |     void(*onConferenceMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_Message_Type, const char *, size_t); | ||||||
|     void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t); |     void(*onConferenceInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t); | ||||||
|     void(*onGroupNameListChange)(ToxWindow *, Tox *, uint32_t); |     void(*onConferenceNameListChange)(ToxWindow *, Tox *, uint32_t); | ||||||
|     void(*onGroupPeerNameChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); |     void(*onConferencePeerNameChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); | ||||||
|     void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); |     void(*onConferenceTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); | ||||||
|     void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); |     void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); | ||||||
|     void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); |     void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); | ||||||
|     void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_File_Control); |     void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_File_Control); | ||||||
| @@ -150,16 +169,9 @@ struct ToxWindow { | |||||||
|     void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int); |     void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int); |     void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int); | ||||||
|  |  | ||||||
|     int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */ |  | ||||||
|     bool is_call; |     bool is_call; | ||||||
|     int ringing_sound; |     int ringing_sound; | ||||||
|  |  | ||||||
| #ifdef VIDEO |  | ||||||
|  |  | ||||||
|     int video_device_selection[2]; /* -1 if not set, if set uses these selections instead of primary video device */ |  | ||||||
|  |  | ||||||
| #endif /* VIDEO */ |  | ||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|     int active_box; /* For box notify */ |     int active_box; /* For box notify */ | ||||||
| @@ -167,13 +179,13 @@ 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; | ||||||
|  |  | ||||||
|     bool is_chat; |     WINDOW_TYPE type; | ||||||
|     bool is_prompt; |  | ||||||
|     bool is_friendlist; |     int show_peerlist;    /* used to toggle conference peerlist */ | ||||||
|     bool is_groupchat; |  | ||||||
|     int show_peerlist;    /* used to toggle groupchat peerlist */ |  | ||||||
|  |  | ||||||
|     WINDOW_ALERTS alert; |     WINDOW_ALERTS alert; | ||||||
|  |  | ||||||
| @@ -182,6 +194,7 @@ struct ToxWindow { | |||||||
|     Help *help; |     Help *help; | ||||||
|  |  | ||||||
|     WINDOW *window; |     WINDOW *window; | ||||||
|  |     WINDOW *window_bar; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* statusbar info holder */ | /* statusbar info holder */ | ||||||
| @@ -218,7 +231,7 @@ struct infobox { | |||||||
|  |  | ||||||
| #define MAX_LINE_HIST 128 | #define MAX_LINE_HIST 128 | ||||||
|  |  | ||||||
| /* chat and groupchat window/buffer holder */ | /* chat and conference window/buffer holder */ | ||||||
| struct ChatContext { | struct ChatContext { | ||||||
|     wchar_t line[MAX_STR_SIZE]; |     wchar_t line[MAX_STR_SIZE]; | ||||||
|     int pos; |     int pos; | ||||||
| @@ -265,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 @@ | |||||||
| /*  group_commands.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,12 +20,15 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #ifndef GROUP_COMMANDS_H | #ifndef X11FOCUS_H | ||||||
| #define GROUP_COMMANDS_H | #define X11FOCUS_H | ||||||
| 
 | 
 | ||||||
| #include "windows.h" | #include <stdbool.h> | ||||||
| #include "toxic.h" |  | ||||||
| 
 | 
 | ||||||
| void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | /* NOTE: If no xlib present don't compile */ | ||||||
| 
 | 
 | ||||||
| #endif /* GROUP_COMMANDS_H */ | int               init_x11focus(void); | ||||||
|  | void              terminate_x11focus(void); | ||||||
|  | bool              is_focused(void); | ||||||
|  | 
 | ||||||
|  | #endif /* X11FOCUS */ | ||||||
							
								
								
									
										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 "xtra.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
|  |  | ||||||
| #ifndef __APPLE__ |  | ||||||
|  |  | ||||||
| #include <X11/Xlib.h> |  | ||||||
| #include <X11/Xatom.h> |  | ||||||
|  |  | ||||||
| #include <pthread.h> |  | ||||||
| #include <assert.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <stdio.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); |  | ||||||
|             usleep(10000); |  | ||||||
|             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