1
0
mirror of https://github.com/Tha14/toxic.git synced 2024-11-22 20:43:02 +01:00

Fix merge conflicts

This commit is contained in:
Jfreegman 2016-06-12 18:27:54 -04:00
commit 43d9623877
No known key found for this signature in database
GPG Key ID: 3627F3144076AE63
32 changed files with 429 additions and 264 deletions

View File

@ -2,12 +2,12 @@
.\" 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.78.1 <http://docbook.sf.net/>
.\" Date: 2015-07-08 .\" Date: 2015-12-07
.\" Manual: Toxic Manual .\" Manual: Toxic Manual
.\" Source: toxic __VERSION__ .\" Source: toxic __VERSION__
.\" Language: English .\" Language: English
.\" .\"
.TH "TOXIC" "1" "2015\-07\-08" "toxic __VERSION__" "Toxic Manual" .TH "TOXIC" "1" "2015\-12\-07" "toxic __VERSION__" "Toxic Manual"
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * Define some portability stuff .\" * Define some portability stuff
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
@ -70,7 +70,7 @@ Encrypt an unencrypted data file\&. An error will occur if this option is used w
Use specified Use specified
\fIdata\-file\fR \fIdata\-file\fR
instead of instead of
\fI~/\&.config/tox/data\fR \fI~/\&.config/tox/toxic_profile\&.tox\fR
.RE .RE
.PP .PP
\-h, \-\-help \-h, \-\-help
@ -127,7 +127,7 @@ __DATADIR__/DHTnodes
Default list of DHT bootstrap nodes\&. Default list of DHT bootstrap nodes\&.
.RE .RE
.PP .PP
~/\&.config/tox/data ~/\&.config/tox/toxic_profile\&.tox
.RS 4 .RS 4
Savestate which contains your personal info (nickname, Tox ID, contacts, etc) Savestate which contains your personal info (nickname, Tox ID, contacts, etc)
.RE .RE
@ -144,7 +144,11 @@ Configuration example\&.
.RE .RE
.SH "BUGS" .SH "BUGS"
.sp .sp
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&. \-Unicode characters with a width larger than 1 column may cause strange behaviour\&.
.sp
\-Text occasionally fails to auto\-scroll to the bottom\&.
.sp
\-Screen flickering sometimes occurs on certain terminals\&.
.SH "AUTHORS" .SH "AUTHORS"
.sp .sp
JFreegman <JFreegman@gmail\&.com> JFreegman <JFreegman@gmail\&.com>
@ -153,6 +157,6 @@ JFreegman <JFreegman@gmail\&.com>
\fBtoxic\&.conf\fR(5) \fBtoxic\&.conf\fR(5)
.SH "LINKS" .SH "LINKS"
.sp .sp
Project page: https://github\&.com/Tox/toxic Project page: https://github\&.com/JFreegman/toxic
.sp .sp
IRC channel: chat\&.freenode\&.net#tox IRC channel: chat\&.freenode\&.net#tox

View File

@ -35,7 +35,7 @@ OPTIONS
is used with an encrypted data file. is used with an encrypted data file.
-f, --file data-file:: -f, --file data-file::
Use specified 'data-file' instead of '~/.config/tox/data' Use specified 'data-file' instead of '~/.config/tox/toxic_profile.tox'
-h, --help:: -h, --help::
Show help message Show help message
@ -71,7 +71,7 @@ FILES
{datadir}/DHTnodes:: {datadir}/DHTnodes::
Default list of DHT bootstrap nodes. Default list of DHT bootstrap nodes.
~/.config/tox/data:: ~/.config/tox/toxic_profile.tox::
Savestate which contains your personal info (nickname, Tox ID, contacts, Savestate which contains your personal info (nickname, Tox ID, contacts,
etc) etc)
@ -83,9 +83,12 @@ FILES
BUGS BUGS
---- ----
Unicode characters with a width larger than 1 column may cause strange -Unicode characters with a width larger than 1 column may cause strange
behaviour. Expect more bugs and bad behaviour: this software is in a behaviour.
pre-alpha stage.
-Text occasionally fails to auto-scroll to the bottom.
-Screen flickering sometimes occurs on certain terminals.
AUTHORS AUTHORS
------- -------
@ -97,6 +100,6 @@ SEE ALSO
LINKS LINKS
----- -----
Project page: <https://github.com/Tox/toxic> Project page: <https://github.com/JFreegman/toxic>
IRC channel: chat.freenode.net#tox IRC channel: chat.freenode.net#tox

View File

@ -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.78.1 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2015-03-28 .\" Date: 2016-02-28
.\" Manual: Toxic Manual .\" Manual: Toxic Manual
.\" Source: toxic __VERSION__ .\" Source: toxic __VERSION__
.\" Language: English .\" Language: English
.\" .\"
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual" .TH "TOXIC\&.CONF" "5" "2016\-02\-28" "toxic __VERSION__" "Toxic Manual"
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * Define some portability stuff .\" * Define some portability stuff
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
@ -113,6 +113,11 @@ Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
Show welcome message on startup\&. true or false Show welcome message on startup\&. true or false
.RE .RE
.PP .PP
\fBshow_connection_msg\fR
.RS 4
Enable friend connection change notifications\&. true or false
.RE
.PP
\fBhistory_size\fR \fBhistory_size\fR
.RS 4 .RS 4
Maximum lines for chat window history\&. Integer value\&. (for example: 700) Maximum lines for chat window history\&. Integer value\&. (for example: 700)
@ -194,6 +199,11 @@ Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
.RS 4 .RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
.RE .RE
.PP
\fBpassword_eval\fR
.RS 4
Replace password prompt by running this command and using its output as the password\&.
.RE
.RE .RE
.PP .PP
\fBsounds\fR \fBsounds\fR
@ -308,6 +318,11 @@ Key combination to scroll contacts list down\&.
.RS 4 .RS 4
Toggle the peer list on and off\&. Toggle the peer list on and off\&.
.RE .RE
.PP
\fBtoggle_paste_mode\fR
.RS 4
Toggle treating linebreaks as enter key press\&.
.RE
.RE .RE
.SH "FILES" .SH "FILES"
.PP .PP
@ -325,7 +340,7 @@ Configuration example\&.
\fBtoxic\fR(1) \fBtoxic\fR(1)
.SH "RESOURCES" .SH "RESOURCES"
.sp .sp
Project page: https://github\&.com/Tox/toxic Project page: https://github\&.com/JFreegman/toxic
.sp .sp
IRC channel: chat\&.freenode\&.net#tox IRC channel: chat\&.freenode\&.net#tox
.SH "AUTHORS" .SH "AUTHORS"

View File

@ -72,6 +72,9 @@ OPTIONS
*show_welcome_msg*;; *show_welcome_msg*;;
Show welcome message on startup. true or false Show welcome message on startup. true or false
*show_connection_msg*;;
Enable friend connection change notifications. true or false
*history_size*;; *history_size*;;
Maximum lines for chat window history. Integer value. (for example: 700) Maximum lines for chat window history. Integer value. (for example: 700)
@ -126,6 +129,10 @@ OPTIONS
*chatlogs_path*;; *chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files. Default path for chatlogs. String value. Absolute path for chatlog files.
*password_eval*;;
Replace password prompt by running this command and using its output as
the password.
*sounds*:: *sounds*::
Configuration related to notification sounds. Configuration related to notification sounds.
Special value "silent" can be used to disable a specific notification. + Special value "silent" can be used to disable a specific notification. +
@ -197,6 +204,9 @@ OPTIONS
*toggle_peerlist*;; *toggle_peerlist*;;
Toggle the peer list on and off. Toggle the peer list on and off.
*toggle_paste_mode*;;
Toggle treating linebreaks as enter key press.
FILES FILES
----- -----
@ -214,7 +224,7 @@ SEE ALSO
RESOURCES RESOURCES
--------- ---------
Project page: <https://github.com/Tox/toxic> Project page: <https://github.com/JFreegman/toxic>
IRC channel: chat.freenode.net#tox IRC channel: chat.freenode.net#tox

View File

@ -1,2 +1,24 @@
173.230.153.129 33445 A992ED1E7E8C6A4C71B12347D9BFA67DD896CF7D5689E20DD94CA066EE384758 173.230.153.129 33445 A992ED1E7E8C6A4C71B12347D9BFA67DD896CF7D5689E20DD94CA066EE384758
cerberus.zodiaclabs.org 33450 B139D5A0280AFA8CD2E5E866226397771F80F9A84967558AD740AE056CB63E58 cerberus.zodiaclabs.org 33450 B139D5A0280AFA8CD2E5E866226397771F80F9A84967558AD740AE056CB63E58
46.101.197.175 443 CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707
95.215.46.114 33445 5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23
5.189.176.217 5190 2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F
148.251.23.146 2306 7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147
104.223.122.15 33445 0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A
78.47.114.252 33445 1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976
d4rk4.ru 1813 53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039
81.4.110.149 33445 9E7BD4793FFECA7F32238FA2361040C09025ED3333744483CA6F3039BFF0211E
95.31.20.151 33445 9CA69BB74DE7C056D1CC6B16AB8A0A38725C0349D187D8996766958584D39340
104.233.104.126 33445 EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414
51.254.84.212 33445 AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D
home.vikingmakt.com.br 33445 188E072676404ED833A4E947DC1D223DF8EFEBE5F5258573A236573688FB9761
5.135.59.163 33445 2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211
185.58.206.164 33445 24156472041E5F220D1FA11D9DF32F7AD697D59845701CDD7BE7D1785EB9DB39
188.244.38.183 33445 15A0F9684E2423F9F46CFA5A50B562AE42525580D840CC50E518192BF333EE38
mrflibble.c4.ee 33445 FAAB17014F42F7F20949F61E55F66A73C230876812A9737F5F6D2DCE4D9E4207
82.211.31.116 33445 AF97B76392A6474AF2FD269220FDCF4127D86A42EF3A242DF53A40A268A2CD7C
128.199.199.197 33445 B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09
103.230.156.174 33445 5C4C7A60183D668E5BD8B3780D1288203E2F1BAE4EEF03278019E21F86174C1D
91.121.66.124 33445 4E3F7D37295664BBD0741B6DBCB6431D6CD77FC4105338C2FC31567BF5C8224A
92.54.84.70 33445 5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802
tox1.privacydragon.me 33445 31910C0497D347FF160D6F3A6C0E317BAFA71E8E03BC4CBB2A185C9D4FB8B31E

View File

@ -1,2 +1 @@
toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25 toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25

View File

@ -29,6 +29,9 @@ ui = {
// true to show the welcome message on startup // true to show the welcome message on startup
show_welcome_msg=true; show_welcome_msg=true;
// true to show friend connection change messages on the home screen
show_connection_msg=true;
// maximum lines for chat window history // maximum lines for chat window history
history_size=700; history_size=700;
@ -90,6 +93,7 @@ sounds = {
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive) // Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
// Note: All printable keys register as input // Note: All printable keys register as input
// Note2: Ctrl+M does not work
keys = { keys = {
next_tab="Ctrl+P"; next_tab="Ctrl+P";
prev_tab="Ctrl+O"; prev_tab="Ctrl+O";
@ -101,5 +105,6 @@ keys = {
peer_list_up="Ctrl+["; peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]"; peer_list_down="Ctrl+]";
toggle_peerlist="Ctrl+b"; toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T";
}; };

View File

@ -103,7 +103,8 @@ void callback_call_canceled ( uint32_t friend_number );
void callback_call_rejected ( uint32_t friend_number ); void callback_call_rejected ( uint32_t friend_number );
void callback_call_ended ( uint32_t friend_number ); void callback_call_ended ( uint32_t friend_number );
void write_device_callback( uint32_t friend_number, const int16_t* PCM, uint16_t size ); void write_device_callback( uint32_t friend_number, const int16_t* PCM, uint16_t sample_count, uint8_t channels,
uint32_t sample_rate );
static void print_err (ToxWindow *self, const char *error_str) static void print_err (ToxWindow *self, const char *error_str)
{ {
@ -120,9 +121,9 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
CallControl.av = toxav_new(tox, &error); CallControl.av = toxav_new(tox, &error);
CallControl.audio_enabled = true; CallControl.audio_enabled = true;
CallControl.audio_bit_rate = 48; CallControl.audio_bit_rate = 64;
CallControl.audio_sample_rate = 48000; CallControl.audio_sample_rate = 48000;
CallControl.audio_frame_duration = 10; CallControl.audio_frame_duration = 20;
CallControl.audio_channels = 1; CallControl.audio_channels = 1;
#ifndef VIDEO #ifndef VIDEO
@ -180,10 +181,11 @@ void read_device_callback(const int16_t* captured, uint32_t size, void* data)
{} {}
} }
void write_device_callback(uint32_t friend_number, const int16_t* PCM, uint16_t size) void write_device_callback(uint32_t friend_number, const int16_t* PCM, uint16_t sample_count, uint8_t channels,
uint32_t sample_rate)
{ {
if ( CallControl.calls[friend_number].ttas ) if ( CallControl.calls[friend_number].ttas )
write_out(CallControl.calls[friend_number].out_idx, PCM, size, CallControl.audio_channels); write_out(CallControl.calls[friend_number].out_idx, PCM, sample_count, channels, sample_rate);
} }
int start_transmission(ToxWindow *self, Call *call) int start_transmission(ToxWindow *self, Call *call)
@ -328,7 +330,7 @@ void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
int16_t const *pcm, size_t sample_count, int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data) uint8_t channels, uint32_t sampling_rate, void *user_data)
{ {
write_device_callback(friend_number, pcm, frame_size); write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate);
} }
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,

View File

@ -357,7 +357,8 @@ DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx
return de_None; return de_None;
} }
inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels) inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t sample_count, uint8_t channels,
uint32_t sample_rate)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
@ -386,7 +387,7 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_
} }
alBufferData(bufid, device->sound_mode, data, length * 2 * channels, device->sample_rate); alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels, sample_rate);
alSourceQueueBuffers(device->source, 1, &bufid); alSourceQueueBuffers(device->source, 1, &bufid);
ALint state; ALint state;

View File

@ -22,7 +22,7 @@
/* /*
* You can have multiple sources (Input devices) but only one output device. * You can have multiple sources (Input devices) but only one output device.
* Pass buffers to output device via write(); * Pass buffers to output device via write();
* Read from running input device(s) via select()/callback combo. * Read from running input device(s) via select()/callback combo.
*/ */
@ -82,7 +82,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
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 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);
void print_devices(ToxWindow* self, DeviceType type); void print_devices(ToxWindow* self, DeviceType type);
void get_primary_device_name(DeviceType type, char *buf, int size); void get_primary_device_name(DeviceType type, char *buf, int size);

View File

@ -53,12 +53,14 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items
} }
/* 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.
e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */ * e.g. if matches contains: [foo, foobar, foe] we put fo in match.
static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n) *
* 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)
{ {
if (n == 1) { if (n == 1) {
strcpy(match, matches[0]); return snprintf(match, match_sz, "%s", matches[0]);
return;
} }
int i; int i;
@ -71,14 +73,14 @@ static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_
char ch2 = matches[j][i]; char ch2 = matches[j][i];
if (ch1 != ch2 || !ch1) { if (ch1 != ch2 || !ch1) {
strcpy(match, matches[0]); snprintf(match, match_sz, "%s", matches[0]);
match[i] = '\0'; match[i] = '\0';
return; return i;
} }
} }
} }
strcpy(match, matches[0]); return snprintf(match, match_sz, "%s", matches[0]);
} }
/* 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,
@ -164,8 +166,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
char match[MAX_STR_SIZE]; char match[MAX_STR_SIZE];
get_str_match(self, match, matches, n_matches); size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
size_t match_len = strlen(match);
if (match_len == 0) {
return 0;
}
if (dir_search) { if (dir_search) {
if (n_matches == 1) if (n_matches == 1)
@ -180,15 +185,15 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int n_endchrs = strlen(endchrs); int n_endchrs = strlen(endchrs);
int strt = ctx->pos - s_len; int strt = ctx->pos - s_len;
int diff = match_len - s_len + n_endchrs; int diff = match_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE) if (ctx->len + diff >= MAX_STR_SIZE)
return -1; return -1;
char tmpend[MAX_STR_SIZE]; char tmpend[MAX_STR_SIZE];
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) {
return -1; return -1;
}
strcpy(&ubuf[strt], match); strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt + match_len], endchrs); strcpy(&ubuf[strt + match_len], endchrs);
@ -197,8 +202,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/* convert to widechar and copy back to original buf */ /* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE]; wchar_t newbuf[MAX_STR_SIZE];
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) {
return -1; return -1;
}
wcscpy(ctx->line, newbuf); wcscpy(ctx->line, newbuf);

View File

@ -216,7 +216,14 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num); get_nick_truncate(m, nick, num);
if (connection_status != TOX_CONNECTION_NONE && statusbar->connection == TOX_CONNECTION_NONE) { TOX_CONNECTION prev_status = statusbar->connection;
statusbar->connection = connection_status;
if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) {
return;
}
if (prev_status == TOX_CONNECTION_NONE) {
Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON
? tox_friend_get_typing(m, num, NULL) : false; ? tox_friend_get_typing(m, num, NULL) : false;
chat_resume_file_senders(self, m, num); chat_resume_file_senders(self, m, num);
@ -236,8 +243,6 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true); write_to_log(msg, nick, ctx->log, true);
} }
statusbar->connection = connection_status;
} }
static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing) static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing)
@ -764,33 +769,6 @@ void chat_onEnd (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
#endif /* SOUND_NOTIFY */ #endif /* SOUND_NOTIFY */
} }
void chat_onRequestTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
return;
self->is_call = false;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No answer!");
#ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound);
#endif /* SOUND_NOTIFY */
}
void chat_onPeerTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
return;
self->is_call = false;
kill_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer disconnected; call ended!");
#ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound);
#endif /* SOUND_NOTIFY */
}
static void init_infobox(ToxWindow *self) static void init_infobox(ToxWindow *self)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -899,7 +877,6 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb; StatusBar *statusbar = self->stb;
@ -910,12 +887,15 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (y2 <= 0 || x2 <= 0) if (y2 <= 0 || x2 <= 0)
return; return;
if (ctx->pastemode && key == '\r')
key = '\n';
if (self->help->active) { if (self->help->active) {
help_onKey(self, key); help_onKey(self, key);
return; return;
} }
if (ltr) { /* char is printable */ if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2); input_new_char(self, key, x, y, x2, y2);
if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE)
@ -957,38 +937,42 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
} }
} else if (key == '\n') { } else if (key == '\r') {
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
char line[MAX_STR_SIZE]; if (!wstring_is_empty(ctx->line))
{
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
add_line_to_hist(ctx); add_line_to_hist(ctx);
if (line[0] == '/') { wstrsubst(ctx->line, L'', L'\n');
if (strcmp(line, "/close") == 0) {
kill_chat_window(self, m); char line[MAX_STR_SIZE] = {0};
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) { if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
send_action(self, ctx, m, line + strlen("/me ")); memset(&line, 0, sizeof(line));
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
kill_chat_window(self, m);
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_action(self, ctx, m, line + strlen("/me "));
} else {
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
}
} else { } else {
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); char selfname[TOX_MAX_NAME_LENGTH];
tox_self_get_name(m, (uint8_t *) selfname);
size_t len = tox_self_get_name_size(m);
selfname[len] = '\0';
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
} }
} else if (!string_is_empty(line)) {
char selfname[TOX_MAX_NAME_LENGTH];
tox_self_get_name(m, (uint8_t *) selfname);
size_t len = tox_self_get_name_size(m);
selfname[len] = '\0';
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
} }
wclear(ctx->linewin); wclear(ctx->linewin);

View File

@ -99,11 +99,6 @@ char *get_user_config_dir(void)
# endif /* __APPLE__ */ # endif /* __APPLE__ */
if (!file_exists(user_config_dir)) {
free(user_config_dir);
return NULL;
}
return user_config_dir; return user_config_dir;
} }

View File

@ -38,47 +38,6 @@ 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
/* Checks for timed out file transfers and closes them. */
#define CHECK_FILE_TIMEOUT_INTERAVAL 5
void check_file_transfer_timeouts(Tox *m)
{
char msg[MAX_STR_SIZE];
static uint64_t last_check = 0;
if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL))
return;
last_check = get_unix_time();
size_t i, j;
for (i = 0; i < Friends.max_idx; ++i) {
if (!Friends.list[i].active)
continue;
for (j = 0; j < MAX_FILES; ++j) {
struct FileTransfer *ft_send = &Friends.list[i].file_sender[j];
if (ft_send->state > FILE_TRANSFER_PAUSED) {
if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) {
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name);
close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
}
}
struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j];
if (ft_recv->state > FILE_TRANSFER_PAUSED) {
if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) {
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name);
close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
}
}
}
}
}
/* 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 */
void init_progress_bar(char *progline) void init_progress_bar(char *progline)

View File

@ -34,7 +34,6 @@
#define GiB 1073741824 /* 1024^3 */ #define GiB 1073741824 /* 1024^3 */
#define MAX_FILES 32 #define MAX_FILES 32
#define TIMEOUT_FILESENDER 120
typedef enum FILE_TRANSFER_STATE { typedef enum FILE_TRANSFER_STATE {
FILE_TRANSFER_INACTIVE, FILE_TRANSFER_INACTIVE,
@ -68,9 +67,6 @@ struct FileTransfer {
uint8_t file_id[TOX_FILE_ID_LENGTH]; uint8_t file_id[TOX_FILE_ID_LENGTH];
}; };
/* Checks for timed out file transfers and closes them. */
void check_file_transfer_timeouts(Tox *m);
/* creates initial progress line that will be updated during file transfer. /* creates initial progress line that will be updated during file transfer.
progline must be at lesat MAX_STR_SIZE bytes */ progline must be at lesat MAX_STR_SIZE bytes */
void init_progress_bar(char *progline); void init_progress_bar(char *progline);

View File

@ -133,17 +133,24 @@ void kill_friendlist(void)
#define TEMP_BLOCKLIST_EXT ".tmp" #define TEMP_BLOCKLIST_EXT ".tmp"
static int save_blocklist(char *path) static int save_blocklist(char *path)
{ {
if (path == NULL) if (path == NULL) {
return -1; return -1;
}
int len = sizeof(BlockedFriend) * Blocked.num_blocked; int len = sizeof(BlockedFriend) * Blocked.num_blocked;
char data[len]; char *data = malloc(len * sizeof(char));
if (data == NULL) {
return -1;
}
int i, count = 0; int i, count = 0;
for (i = 0; i < Blocked.max_idx; ++i) { for (i = 0; i < Blocked.max_idx; ++i) {
if (count > Blocked.num_blocked) if (count > Blocked.num_blocked) {
free(data);
return -1; return -1;
}
if (Blocked.list[i].active) { if (Blocked.list[i].active) {
BlockedFriend tmp; BlockedFriend tmp;
@ -164,8 +171,11 @@ static int save_blocklist(char *path)
/* Blocklist is empty, we can remove the empty file */ /* Blocklist is empty, we can remove the empty file */
if (count == 0) { if (count == 0) {
if (remove(path) != 0) free(data);
if (remove(path) != 0) {
return -1; return -1;
}
return 0; return 0;
} }
@ -175,19 +185,24 @@ static int save_blocklist(char *path)
FILE *fp = fopen(temp_path, "wb"); FILE *fp = fopen(temp_path, "wb");
if (fp == NULL) if (fp == NULL) {
free(data);
return -1; return -1;
}
if (fwrite(data, len, 1, fp) != 1) { if (fwrite(data, len, 1, fp) != 1) {
fprintf(stderr, "Failed to write blocklist data.\n"); fprintf(stderr, "Failed to write blocklist data.\n");
fclose(fp); fclose(fp);
free(data);
return -1; return -1;
} }
fclose(fp); fclose(fp);
free(data);
if (rename(temp_path, path) != 0) if (rename(temp_path, path) != 0) {
return -1; return -1;
}
return 0; return 0;
} }
@ -753,7 +768,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
return; return;
switch (key) { switch (key) {
case '\n': case '\r':
if (blocklist_view) if (blocklist_view)
break; break;

View File

@ -36,6 +36,7 @@
#include "avatars.h" #include "avatars.h"
#include "name_lookup.h" #include "name_lookup.h"
#include "qr_code.h" #include "qr_code.h"
#include "toxic_strings.h"
extern char *DATA_FILE; extern char *DATA_FILE;
extern ToxWindow *prompt; extern ToxWindow *prompt;

View File

@ -1090,7 +1090,10 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
return; return;
} }
if (ltr) { /* char is printable */ if (ctx->pastemode && key == '\r')
key = '\n';
if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2); input_new_char(self, key, x, y, x2, y2);
return; return;
} }
@ -1133,40 +1136,43 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == user_settings->key_peer_list_up) { } else if (key == user_settings->key_peer_list_up) {
if (chat->side_pos > 0) if (chat->side_pos > 0)
--chat->side_pos; --chat->side_pos;
} else if (key == '\n') { } else if (key == '\r') {
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
char line[MAX_STR_SIZE]; if (!wstring_is_empty(ctx->line)) {
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
add_line_to_hist(ctx); add_line_to_hist(ctx);
if (line[0] == '/') { wstrsubst(ctx->line, L'', L'\n');
if (strncmp(line, "/close", strlen("/close")) == 0) {
int offset = 6;
if (line[offset] != '\0') char line[MAX_STR_SIZE];
++offset;
exit_groupchat(self, m, self->num, line + offset, ctx->len - offset); if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
return; memset(&line, 0, sizeof(line));
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_message(self, m, self->num, line + 4, TOX_MESSAGE_TYPE_ACTION); if (line[0] == '/') {
} else if (strncmp(line, "/whisper ", strlen("/whisper ")) == 0) { if (strncmp(line, "/close", strlen("/close")) == 0) {
send_group_prvt_message(self, m, self->num, line + 9, ctx->len - 9); int offset = 6;
if (line[offset] != '\0')
++offset;
exit_groupchat(self, m, self->num, line + offset, ctx->len - offset);
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_message(self, m, self->num, line + 4, TOX_MESSAGE_TYPE_ACTION);
} else if (strncmp(line, "/whisper ", strlen("/whisper ")) == 0) {
send_group_prvt_message(self, m, self->num, line + 9, ctx->len - 9);
} else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
}
} else { } else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); send_group_message(self, m, self->num, line, TOX_MESSAGE_TYPE_NORMAL);
} }
} else if (!string_is_empty(line)) {
send_group_message(self, m, self->num, line, TOX_MESSAGE_TYPE_NORMAL);
}
wclear(ctx->linewin); wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2 - CURS_Y_OFFSET, 0);
reset_buf(ctx); reset_buf(ctx);
}
} }
} }

View File

@ -245,7 +245,9 @@ static void help_draw_keys(ToxWindow *self)
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+[ and Ctrl+] : Scroll peer list in groupchats\n"); wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n\n"); wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n");
wprintw(win, " Ctrl+J : Insert new line\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");
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
@ -360,7 +362,7 @@ void help_onKey(ToxWindow *self, wint_t key)
break; break;
case 'k': case 'k':
help_init_window(self, 13, 80); help_init_window(self, 15, 80);
self->help->type = HELP_KEYS; self->help->type = HELP_KEYS;
break; break;

View File

@ -42,9 +42,12 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
/* this is the only place we need to do this check */
if (key == '\n')
key = L'';
int cur_len = wcwidth(key); int cur_len = wcwidth(key);
/* this is the only place we need to do this check */
if (cur_len == -1) { if (cur_len == -1) {
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
return; return;
@ -266,15 +269,19 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
/* TODO: this special case is ugly. /* TODO: this special case is ugly.
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 && key == user_settings->key_toggle_peerlist) { if (!match) {
if (self->is_groupchat) { if (key == user_settings->key_toggle_peerlist) {
self->show_peerlist ^= 1; if (self->is_groupchat) {
redraw_groupchat_win(self); self->show_peerlist ^= 1;
redraw_groupchat_win(self);
}
match = true;
}
else if (key == user_settings->key_toggle_pastemode) {
self->chatwin->pastemode ^= 1;
match = true;
} }
match = true;
} }
return match; return match;
} }

View File

@ -333,17 +333,27 @@ void line_info_print(ToxWindow *self)
line->name1); line->name1);
wattroff(win, COLOR_PAIR(nameclr)); wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>') char* msg = line->msg;
wattron(win, COLOR_PAIR(GREEN)); while (msg)
else if (line->msg[0] == '<') {
wattron(win, COLOR_PAIR(RED)); char* line = strsep(&msg, "\n");
wprintw(win, "%s", line->msg); if (line[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
else if (line[0] == '<')
wattron(win, COLOR_PAIR(RED));
if (line->msg[0] == '>') wprintw(win, "%s%c", line, msg ? '\n' : '\0');
wattroff(win, COLOR_PAIR(GREEN));
else if (line->msg[0] == '<') if (line[0] == '>')
wattroff(win, COLOR_PAIR(RED)); 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 (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED)); wattron(win, COLOR_PAIR(RED));

View File

@ -194,6 +194,15 @@ int string_is_empty(const char *string)
return string[0] == '\0'; return string[0] == '\0';
} }
/* Returns 1 if the string is empty, 0 otherwise */
int wstring_is_empty(const wchar_t *string)
{
if (!string)
return true;
return string[0] == L'\0';
}
/* convert a multibyte string to a wide character string and puts in buf. */ /* convert a multibyte string to a wide character string and puts in buf. */
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
{ {

View File

@ -81,6 +81,9 @@ void update_unix_time(void);
/* 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);
/* Same as above but for wide character strings */
int wstring_is_empty(const wchar_t *string);
/* convert a multibyte string to a wide character string (must provide buffer) */ /* convert a multibyte string to a wide character string (must provide buffer) */
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);

View File

@ -234,17 +234,17 @@ void* do_playing(void* _p)
} }
bool has_looping = false; bool has_looping = 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) has_looping = true; if (actives[i].looping) has_looping = true;
test_active_notify = actives[i].active && !actives[i].looping;
if (actives[i].active && !actives[i].looping #ifdef BOX_NOTIFY
#ifdef BOX_NOTIFY test_active_notify = test_active_notify && !actives[i].box;
&& !actives[i].box #endif
#endif if (test_active_notify) {
) {
if(actives[i].id_indicator) if(actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */ *actives[i].id_indicator = -1; /* reset indicator value */
@ -580,7 +580,6 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
alDeleteSources(1, &actives[id].source); alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer); alDeleteBuffers(1,&actives[id].buffer);
alGenSources(1, &actives[id].source); alGenSources(1, &actives[id].source);
alGenBuffers(1, &actives[id].buffer); alGenBuffers(1, &actives[id].buffer);
actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]); actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]);

View File

@ -190,13 +190,16 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (x2 <= 0 || y2 <= 0) if (x2 <= 0 || y2 <= 0)
return; return;
if (ctx->pastemode && key == '\r')
key = '\n';
/* ignore non-menu related input if active */ /* ignore non-menu related input if active */
if (self->help->active) { if (self->help->active) {
help_onKey(self, key); help_onKey(self, key);
return; return;
} }
if (ltr) { /* char is printable */ if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2); input_new_char(self, key, x, y, x2, y2);
return; return;
} }
@ -233,19 +236,22 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else { } else {
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
} }
} else if (key == '\n') { } else if (key == '\r') {
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
char line[MAX_STR_SIZE] = {0}; if (!wstring_is_empty(ctx->line))
{
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
add_line_to_hist(ctx); add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n');
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); char line[MAX_STR_SIZE] = {0};
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
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 - CURS_Y_OFFSET, 0);
@ -384,6 +390,10 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
const char *msg; const char *msg;
if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) {
return;
}
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, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);

View File

@ -57,6 +57,7 @@ static struct ui_strings {
const char* show_typing_self; const char* show_typing_self;
const char* show_typing_other; const char* show_typing_other;
const char* show_welcome_msg; const char* show_welcome_msg;
const char* show_connection_msg;
const char* line_join; const char* line_join;
const char* line_quit; const char* line_quit;
@ -79,6 +80,7 @@ static struct ui_strings {
"show_typing_self", "show_typing_self",
"show_typing_other", "show_typing_other",
"show_welcome_msg", "show_welcome_msg",
"show_connection_msg",
"line_join", "line_join",
"line_quit", "line_quit",
"line_alert", "line_alert",
@ -101,6 +103,7 @@ static void ui_defaults(struct user_settings* settings)
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;
settings->show_connection_msg = SHOW_CONNECTION_MSG_ON;
snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN); snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
@ -127,6 +130,7 @@ static const struct keys_strings {
const char* peer_list_up; const char* peer_list_up;
const char* peer_list_down; const char* peer_list_down;
const char* toggle_peerlist; const char* toggle_peerlist;
const char* toggle_pastemode;
} key_strings = { } key_strings = {
"keys", "keys",
"next_tab", "next_tab",
@ -139,6 +143,7 @@ static const struct keys_strings {
"peer_list_up", "peer_list_up",
"peer_list_down", "peer_list_down",
"toggle_peerlist", "toggle_peerlist",
"toggle_paste_mode",
}; };
/* defines from toxic.h */ /* defines from toxic.h */
@ -154,6 +159,7 @@ static void key_defaults(struct user_settings* settings)
settings->key_peer_list_up = T_KEY_C_LB; settings->key_peer_list_up = T_KEY_C_LB;
settings->key_peer_list_down = T_KEY_C_RB; settings->key_peer_list_down = T_KEY_C_RB;
settings->key_toggle_peerlist = T_KEY_C_B; settings->key_toggle_peerlist = T_KEY_C_B;
settings->key_toggle_pastemode = T_KEY_C_T;
} }
static const struct tox_strings { static const struct tox_strings {
@ -161,11 +167,13 @@ static const struct tox_strings {
const char* download_path; const char* download_path;
const char* chatlogs_path; const char* chatlogs_path;
const char* avatar_path; const char* avatar_path;
const char* password_eval;
} tox_strings = { } tox_strings = {
"tox", "tox",
"download_path", "download_path",
"chatlogs_path", "chatlogs_path",
"avatar_path", "avatar_path",
"password_eval",
}; };
static void tox_defaults(struct user_settings* settings) static void tox_defaults(struct user_settings* settings)
@ -173,6 +181,7 @@ static void tox_defaults(struct user_settings* settings)
strcpy(settings->download_path, ""); strcpy(settings->download_path, "");
strcpy(settings->chatlogs_path, ""); strcpy(settings->chatlogs_path, "");
strcpy(settings->avatar_path, ""); strcpy(settings->avatar_path, "");
strcpy(settings->password_eval, "");
} }
#ifdef AUDIO #ifdef AUDIO
@ -224,11 +233,11 @@ static const struct sound_strings {
}; };
#endif #endif
static int key_parse(const char** bind){ static int key_parse(const char **bind) {
int len = strlen(*bind); int len = strlen(*bind);
if (len > 5) { if (len > 5) {
if(strncasecmp(*bind, "ctrl+", 5) == 0) if(strncasecmp(*bind, "ctrl+", 5) == 0 && toupper(bind[0][5]) != 'M') /* ctrl+m cannot be used */
return toupper(bind[0][5]) - 'A' + 1; return toupper(bind[0][5]) - 'A' + 1;
} }
@ -241,6 +250,14 @@ static int key_parse(const char** bind){
return -1; return -1;
} }
static void set_key_binding(int *key, const char **bind) {
int code = key_parse(bind);
if (code != -1) {
*key = code;
}
}
int settings_load(struct user_settings *s, const char *patharg) int settings_load(struct user_settings *s, const char *patharg)
{ {
config_t cfg[1]; config_t cfg[1];
@ -311,6 +328,7 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self); config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self);
config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other); config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other);
config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg); config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg);
if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) { if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) {
snprintf(s->line_join, sizeof(s->line_join), "%s", str); snprintf(s->line_join, sizeof(s->line_join), "%s", str);
@ -365,31 +383,41 @@ int settings_load(struct user_settings *s, const char *patharg)
if (len >= sizeof(s->avatar_path)) if (len >= sizeof(s->avatar_path))
s->avatar_path[0] = '\0'; s->avatar_path[0] = '\0';
} }
if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) {
snprintf(s->password_eval, sizeof(s->password_eval), "%s", str);
int len = strlen(str);
if (len >= sizeof(s->password_eval))
s->password_eval[0] = '\0';
}
} }
/* keys */ /* keys */
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) { if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
const char* tmp = NULL; const char* tmp = NULL;
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp))
s->key_next_tab = key_parse(&tmp); set_key_binding(&s->key_next_tab, &tmp);
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp))
s->key_prev_tab = key_parse(&tmp); set_key_binding(&s->key_prev_tab, &tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp))
s->key_scroll_line_up = key_parse(&tmp); set_key_binding(&s->key_scroll_line_up, &tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp))
s->key_scroll_line_down= key_parse(&tmp); set_key_binding(&s->key_scroll_line_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp))
s->key_half_page_up = key_parse(&tmp); set_key_binding(&s->key_half_page_up, &tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp))
s->key_half_page_down = key_parse(&tmp); set_key_binding(&s->key_half_page_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp))
s->key_page_bottom = key_parse(&tmp); set_key_binding(&s->key_page_bottom, &tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp))
s->key_peer_list_up = key_parse(&tmp); set_key_binding(&s->key_peer_list_up, &tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp))
s->key_peer_list_down = key_parse(&tmp); set_key_binding(&s->key_peer_list_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp)) if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp))
s->key_toggle_peerlist = key_parse(&tmp); set_key_binding(&s->key_toggle_peerlist, &tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp))
set_key_binding(&s->key_toggle_pastemode, &tmp);
} }
#ifdef AUDIO #ifdef AUDIO

View File

@ -30,6 +30,8 @@
/* Represents line_* hints max strlen */ /* Represents line_* hints max strlen */
#define LINE_HINT_MAX 3 #define LINE_HINT_MAX 3
#define PASSWORD_EVAL_MAX 512
/* holds user setting values */ /* holds user setting values */
struct user_settings { struct user_settings {
int autolog; /* boolean */ int autolog; /* boolean */
@ -44,6 +46,7 @@ struct user_settings {
int show_typing_self; /* boolean */ int show_typing_self; /* boolean */
int show_typing_other; /* boolean */ int show_typing_other; /* boolean */
int show_welcome_msg; /* boolean */ int show_welcome_msg; /* boolean */
int show_connection_msg; /* boolean */
char line_join[LINE_HINT_MAX + 1]; char line_join[LINE_HINT_MAX + 1];
char line_quit[LINE_HINT_MAX + 1]; char line_quit[LINE_HINT_MAX + 1];
@ -54,6 +57,7 @@ struct user_settings {
char download_path[PATH_MAX]; char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX]; char chatlogs_path[PATH_MAX];
char avatar_path[PATH_MAX]; char avatar_path[PATH_MAX];
char password_eval[PASSWORD_EVAL_MAX];
int key_next_tab; int key_next_tab;
int key_prev_tab; int key_prev_tab;
@ -65,6 +69,7 @@ struct user_settings {
int key_peer_list_up; int key_peer_list_up;
int key_peer_list_down; int key_peer_list_down;
int key_toggle_peerlist; int key_toggle_peerlist;
int key_toggle_pastemode;
int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */ int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */
char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH]; char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH];
@ -95,6 +100,9 @@ enum {
SHOW_WELCOME_MSG_OFF = 0, SHOW_WELCOME_MSG_OFF = 0,
SHOW_WELCOME_MSG_ON = 1, SHOW_WELCOME_MSG_ON = 1,
SHOW_CONNECTION_MSG_OFF = 0,
SHOW_CONNECTION_MSG_ON = 1,
DFLT_HST_SIZE = 700, DFLT_HST_SIZE = 700,
MPLEX_OFF = 0, MPLEX_OFF = 0,

View File

@ -39,6 +39,7 @@
#include <unistd.h> #include <unistd.h>
#include <limits.h> #include <limits.h>
#include <termios.h> #include <termios.h>
#include <ctype.h>
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
@ -200,6 +201,7 @@ static void init_term(void)
cbreak(); cbreak();
keypad(stdscr, 1); keypad(stdscr, 1);
noecho(); noecho();
nonl();
timeout(100); timeout(100);
if (has_colors()) { if (has_colors()) {
@ -484,6 +486,45 @@ static int password_prompt(char *buf, int size)
return len; return len;
} }
/* Get the password from the eval command.
* return length of password on success, 0 on failure
*/
static int password_eval(char *buf, int size)
{
buf[0] = '\0';
/* Run password_eval command */
FILE *f = popen(user_settings->password_eval, "r");
if (f == NULL) {
fprintf(stderr, "Executing password_eval failed\n");
return 0;
}
/* Get output from command */
char *ret = fgets(buf, size, f);
if (ret == NULL) {
fprintf(stderr, "Reading password from password_eval command failed\n");
pclose(f);
return 0;
}
/* Get exit status */
int status = pclose(f);
if (status != 0) {
fprintf(stderr, "password_eval command returned error %d\n", status);
return 0;
}
/* Removez whitespace or \n at end */
int i, len = strlen(buf);
for (i = len - 1; i > 0 && isspace(buf[i]); i--) {
buf[i] = 0;
len--;
}
return len;
}
/* Ask user if they would like to encrypt the data file and set password */ /* Ask user if they would like to encrypt the data file and set password */
static void first_time_encrypt(const char *msg) static void first_time_encrypt(const char *msg)
{ {
@ -555,25 +596,36 @@ static void first_time_encrypt(const char *msg)
#define TEMP_PROFILE_EXT ".tmp" #define TEMP_PROFILE_EXT ".tmp"
int store_data(Tox *m, const char *path) int store_data(Tox *m, const char *path)
{ {
if (path == NULL) if (path == NULL) {
return -1; return -1;
}
char temp_path[strlen(path) + strlen(TEMP_PROFILE_EXT) + 1]; char temp_path[strlen(path) + strlen(TEMP_PROFILE_EXT) + 1];
snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_PROFILE_EXT); snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_PROFILE_EXT);
FILE *fp = fopen(temp_path, "wb"); FILE *fp = fopen(temp_path, "wb");
if (fp == NULL) if (fp == NULL) {
return -1; return -1;
}
size_t data_len = tox_get_savedata_size(m); size_t data_len = tox_get_savedata_size(m);
char data[data_len]; char *data = malloc(data_len * sizeof(char));
if (data == NULL) {
return -1;
}
tox_get_savedata(m, (uint8_t *) data); tox_get_savedata(m, (uint8_t *) data);
if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) { if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) {
size_t enc_len = data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH; size_t enc_len = data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
char enc_data[enc_len]; char *enc_data = malloc(enc_len * sizeof(char));
if (enc_data == NULL) {
free(data);
return -1;
}
TOX_ERR_ENCRYPTION err; TOX_ERR_ENCRYPTION err;
tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len, tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len,
@ -582,26 +634,33 @@ 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(data);
free(enc_data);
return -1; return -1;
} }
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(data);
free(enc_data);
return -1; return -1;
} }
} else { /* data will not be encrypted */ } else { /* data will not be encrypted */
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(data);
return -1; return -1;
} }
} }
fclose(fp); fclose(fp);
free(data);
if (rename(temp_path, path) != 0) if (rename(temp_path, path) != 0) {
return -1; return -1;
}
return 0; return 0;
} }
@ -714,14 +773,21 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
user_password.data_is_encrypted = true; user_password.data_is_encrypted = true;
size_t pwlen = 0; size_t pwlen = 0;
system("clear"); // TODO: is this portable? int pweval = user_settings->password_eval[0];
printf("Enter password (q to quit) "); if (!pweval) {
system("clear"); // TODO: is this portable?
printf("Enter password (q to quit) ");
}
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[plain_len];
while (true) { while (true) {
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass)); if (pweval) {
pwlen = password_eval(user_password.pass, sizeof(user_password.pass));
} else {
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
}
user_password.len = pwlen; user_password.len = pwlen;
if (strcasecmp(user_password.pass, "q") == 0) { if (strcasecmp(user_password.pass, "q") == 0) {
@ -733,6 +799,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
system("clear"); system("clear");
sleep(1); sleep(1);
printf("Invalid password. Try again. "); printf("Invalid password. Try again. ");
pweval = 0;
continue; continue;
} }
@ -757,6 +824,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
system("clear"); system("clear");
sleep(1); sleep(1);
printf("Invalid password. Try again. "); printf("Invalid password. Try again. ");
pweval = 0;
} else { } else {
fclose(fp); fclose(fp);
exit_toxic_err("tox_pass_decrypt() failed", pwerr); exit_toxic_err("tox_pass_decrypt() failed", pwerr);
@ -859,7 +927,6 @@ static void do_toxic(Tox *m, ToxWindow *prompt)
tox_iterate(m); tox_iterate(m);
do_bootstrap(m); do_bootstrap(m);
check_file_transfer_timeouts(m);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
} }
@ -1198,25 +1265,6 @@ static void init_default_data_files(void)
free(user_config_dir); free(user_config_dir);
} }
#define REC_TOX_DO_LOOPS_PER_SEC 25
/* Adjusts usleep value so that tox_do runs close to the recommended number of times per second */
static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, uint64_t cur_time, useconds_t msleepval)
{
useconds_t new_sleep = MAX(msleepval, 3);
++(*loopcount);
if (*looptimer == cur_time)
return new_sleep;
if (*loopcount != REC_TOX_DO_LOOPS_PER_SEC)
new_sleep *= (double) *loopcount / REC_TOX_DO_LOOPS_PER_SEC;
*looptimer = cur_time;
*loopcount = 0;
return new_sleep;
}
// this doesn't do anything (yet) // this doesn't do anything (yet)
#ifdef X11 #ifdef X11
void DnD_callback(const char* asdv, DropType dt) void DnD_callback(const char* asdv, DropType dt)
@ -1350,9 +1398,6 @@ int main(int argc, char **argv)
execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);
uint64_t last_save = (uint64_t) time(NULL); uint64_t last_save = (uint64_t) time(NULL);
uint64_t looptimer = last_save;
useconds_t msleepval = 40000;
uint64_t loopcount = 0;
while (true) { while (true) {
do_toxic(m, prompt); do_toxic(m, prompt);
@ -1368,8 +1413,7 @@ int main(int argc, char **argv)
last_save = cur_time; last_save = cur_time;
} }
msleepval = optimal_msleepval(&looptimer, &loopcount, cur_time, msleepval); usleep(tox_iteration_interval(m) * 1000);
usleep(msleepval);
} }
return 0; return 0;

View File

@ -70,6 +70,7 @@
#define T_KEY_C_L 0x0C /* ctrl-l */ #define T_KEY_C_L 0x0C /* ctrl-l */
#define T_KEY_C_W 0x17 /* ctrl-w */ #define T_KEY_C_W 0x17 /* ctrl-w */
#define T_KEY_C_B 0x02 /* ctrl-b */ #define T_KEY_C_B 0x02 /* ctrl-b */
#define T_KEY_C_T 0x14 /* ctrl-t */
#define T_KEY_TAB 0x09 /* TAB key */ #define T_KEY_TAB 0x09 /* TAB key */
#define ONLINE_CHAR "*" #define ONLINE_CHAR "*"

View File

@ -166,19 +166,19 @@ void reset_buf(ChatContext *ctx)
ctx->start = 0; ctx->start = 0;
} }
/* Removes trailing spaces from line. */ /* Removes trailing spaces and newlines from line. */
void rm_trailing_spaces_buf(ChatContext *ctx) void rm_trailing_spaces_buf(ChatContext *ctx)
{ {
if (ctx->len <= 0) if (ctx->len <= 0)
return; return;
if (ctx->line[ctx->len - 1] != ' ') if (ctx->line[ctx->len - 1] != ' ' && ctx->line[ctx->len - 1] != L'')
return; return;
int i; int i;
for (i = ctx->len - 1; i >= 0; --i) { for (i = ctx->len - 1; i >= 0; --i) {
if (ctx->line[i] != ' ') if (ctx->line[i] != ' ' && ctx->line[i] != L'')
break; break;
} }
@ -242,3 +242,19 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
ctx->pos = h_len; ctx->pos = h_len;
ctx->len = h_len; ctx->len = h_len;
} }
void strsubst(char* str, char old, char new)
{
int i;
for (i = 0; str[i] != '\0'; ++i)
if (str[i] == old)
str[i] = new;
}
void wstrsubst(wchar_t* str, wchar_t old, wchar_t new)
{
int i;
for (i = 0; str[i] != L'\0'; ++i)
if (str[i] == old)
str[i] = new;
}

View File

@ -66,4 +66,8 @@ void add_line_to_hist(ChatContext *ctx);
resets line if at end of history */ resets line if at end of history */
void fetch_hist_item(ChatContext *ctx, int key_dir); void fetch_hist_item(ChatContext *ctx, int key_dir);
/* Substitutes all occurrences of old with new. */
void strsubst(char *str, char old, char new);
void wstrsubst(wchar_t *str, wchar_t old, wchar_t new);
#endif /* #define TOXIC_STRINGS_H */ #endif /* #define TOXIC_STRINGS_H */

View File

@ -247,6 +247,7 @@ struct ChatContext {
#endif #endif
uint8_t self_is_typing; uint8_t self_is_typing;
uint8_t pastemode; /* whether to translate \r to \n */
WINDOW *history; WINDOW *history;
WINDOW *linewin; WINDOW *linewin;