From 53e4b2c971a319aff2819ae1ebec2b33a0126351 Mon Sep 17 00:00:00 2001 From: Loui Chang Date: Thu, 19 Feb 2015 18:20:38 -0500 Subject: [PATCH 1/3] Add support for custom timestamps in chat and logs. Internal representations of the time_format setting are replaced by timestamp format strings. time_format is only used in the setup. Signed-off-by: Loui Chang --- doc/toxic.conf.5 | 26 +++++++++++++++++++------- doc/toxic.conf.5.asc | 15 ++++++++++++--- misc/toxic.conf.example | 3 +++ src/friendlist.c | 2 +- src/line_info.c | 14 +++++++------- src/log.c | 2 +- src/misc_tools.c | 2 +- src/settings.c | 31 ++++++++++++++++++++++++++----- src/settings.h | 10 ++++++---- src/toxic.h | 2 +- 10 files changed, 77 insertions(+), 30 deletions(-) diff --git a/doc/toxic.conf.5 b/doc/toxic.conf.5 index fa30e86..0ee128a 100644 --- a/doc/toxic.conf.5 +++ b/doc/toxic.conf.5 @@ -2,12 +2,12 @@ .\" Title: toxic.conf .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 2015-02-08 +.\" Date: 2015-02-19 .\" Manual: Toxic Manual .\" Source: toxic __VERSION__ .\" Language: English .\" -.TH "TOXIC\&.CONF" "5" "2015\-02\-08" "toxic __VERSION__" "Toxic Manual" +.TH "TOXIC\&.CONF" "5" "2015\-02\-19" "toxic __VERSION__" "Toxic Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -66,6 +66,23 @@ Configuration related to interface elements\&. Enable or disable timestamps\&. true or false .RE .PP +\fBtime_format\fR +.RS 4 +Select between 24 and 12 hour time\&. Specify 24 or 12\&. Setting timestamp_format and log_timestamp_format will override this setting\&. +.RE +.PP +\fBtimestamp_format\fR +.RS 4 +Time format string for the interface enclosed by double quotes\&. See +\fBdate\fR(1) +.RE +.PP +\fBlog_timestamp_format\fR +.RS 4 +Time format string for logging enclosed by double quotes\&. See +\fBdate\fR(1) +.RE +.PP \fBalerts\fR .RS 4 Enable or disable terminal alerts on events\&. true or false @@ -81,11 +98,6 @@ Select between native terminal colors and toxic color theme\&. true or false Enable or disable autologging\&. true or false .RE .PP -\fBtime_format\fR -.RS 4 -Select between 24 and 12 hour time\&. Specify 24 or 12 -.RE -.PP \fBshow_typing_other\fR .RS 4 Show when others are typing in a 1\-on\-1 chat\&. true or false diff --git a/doc/toxic.conf.5.asc b/doc/toxic.conf.5.asc index 4643405..5411bba 100644 --- a/doc/toxic.conf.5.asc +++ b/doc/toxic.conf.5.asc @@ -42,6 +42,18 @@ OPTIONS *timestamps*;; Enable or disable timestamps. true or false + *time_format*;; + Select between 24 and 12 hour time. Specify 24 or 12. Setting + timestamp_format and log_timestamp_format will override this setting. + + *timestamp_format*;; + Time format string for the interface enclosed by double quotes. + See *date*(1) + + *log_timestamp_format*;; + Time format string for logging enclosed by double quotes. + See *date*(1) + *alerts*;; Enable or disable terminal alerts on events. true or false @@ -51,9 +63,6 @@ OPTIONS *autolog*;; Enable or disable autologging. true or false - *time_format*;; - Select between 24 and 12 hour time. Specify 24 or 12 - *show_typing_other*;; Show when others are typing in a 1-on-1 chat. true or false diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index 7b8e352..ed2e24f 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -17,6 +17,9 @@ ui = { // 24 or 12 hour time time_format=24; + // timestamp format string according to date/strftime format. Overrides time_format setting + timestamp_format="%H:%M:%S"; + // true to show you when others are typing a message in 1-on-1 chats show_typing_other=true; diff --git a/src/friendlist.c b/src/friendlist.c index 21f8a93..f0d4fca 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -299,7 +299,7 @@ static void update_friend_last_online(int32_t num, uint64_t timestamp) Friends.list[num].last_online.tm = *localtime((const time_t*)×tamp); /* if the format changes make sure TIME_STR_SIZE is the correct size */ - const char *t = user_settings->time == TIME_12 ? "%I:%M %p" : "%H:%M"; + const char *t = user_settings->timestamp_format; strftime(Friends.list[num].last_online.hour_min_str, TIME_STR_SIZE, t, &Friends.list[num].last_online.tm); } diff --git a/src/line_info.c b/src/line_info.c index d3be1ae..70b943a 100644 --- a/src/line_info.c +++ b/src/line_info.c @@ -202,7 +202,7 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons if (timestr) { snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr); - len += strlen(new_line->timestr); + len += strlen(new_line->timestr) + 1; } if (name1) { @@ -302,7 +302,7 @@ void line_info_print(ToxWindow *self) case OUT_MSG_READ: case IN_MSG: wattron(win, COLOR_PAIR(BLUE)); - wprintw(win, "%s", line->timestr); + wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); int nameclr = GREEN; @@ -342,7 +342,7 @@ void line_info_print(ToxWindow *self) case OUT_ACTION: case IN_ACTION: wattron(win, COLOR_PAIR(BLUE)); - wprintw(win, "%s", line->timestr); + wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(YELLOW)); @@ -366,7 +366,7 @@ void line_info_print(ToxWindow *self) case SYS_MSG: if (line->timestr[0]) { wattron(win, COLOR_PAIR(BLUE)); - wprintw(win, "%s", line->timestr); + wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); } @@ -399,7 +399,7 @@ void line_info_print(ToxWindow *self) case CONNECTION: wattron(win, COLOR_PAIR(BLUE)); - wprintw(win, "%s", line->timestr); + wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(line->colour)); @@ -416,7 +416,7 @@ void line_info_print(ToxWindow *self) case DISCONNECTION: wattron(win, COLOR_PAIR(BLUE)); - wprintw(win, "%s", line->timestr); + wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(line->colour)); @@ -433,7 +433,7 @@ void line_info_print(ToxWindow *self) case NAME_CHANGE: wattron(win, COLOR_PAIR(BLUE)); - wprintw(win, "%s", line->timestr); + wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(MAGENTA)); diff --git a/src/log.c b/src/log.c index 27e2a20..f04c83d 100644 --- a/src/log.c +++ b/src/log.c @@ -134,7 +134,7 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e else snprintf(name_frmt, sizeof(name_frmt), "%s:", name); - const char *t = user_settings->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]"; + const char *t = user_settings->log_timestamp_format; char s[MAX_STR_SIZE]; strftime(s, MAX_STR_SIZE, t, get_time()); fprintf(log->file, "%s %s %s\n", s, name_frmt, msg); diff --git a/src/misc_tools.c b/src/misc_tools.c index 0b977b2..1c48f32 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -87,7 +87,7 @@ void get_time_str(char *buf, int bufsize) return; } - const char *t = user_settings->time == TIME_12 ? "%I:%M:%S " : "%H:%M:%S "; + const char *t = user_settings->timestamp_format; strftime(buf, bufsize, t, get_time()); } diff --git a/src/settings.c b/src/settings.c index cd8385f..ca30f3e 100644 --- a/src/settings.c +++ b/src/settings.c @@ -47,10 +47,12 @@ static struct ui_strings { const char* self; const char* timestamps; + const char* time_format; + const char* timestamp_format; + const char* log_timestamp_format; const char* alerts; const char* native_colors; const char* autolog; - const char* time_format; const char* history_size; const char* show_typing_self; const char* show_typing_other; @@ -63,10 +65,12 @@ static struct ui_strings { } ui_strings = { "ui", "timestamps", + "time_format", + "timestamp_format", + "log_timestamp_format", "alerts", "native_colors", "autolog", - "time_format", "history_size", "show_typing_self", "show_typing_other", @@ -80,7 +84,9 @@ static struct ui_strings { static void ui_defaults(struct user_settings* settings) { settings->timestamps = TIMESTAMPS_ON; - settings->time = TIME_24; + snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT); + snprintf(settings->log_timestamp_format, sizeof(settings->log_timestamp_format), "%s", LOG_TIMESTAMP_DEFAULT); + settings->autolog = AUTOLOG_OFF; settings->alerts = ALERTS_ENABLED; settings->colour_theme = DFLT_COLS; @@ -267,6 +273,23 @@ int settings_load(struct user_settings *s, const char *patharg) /* ui */ if ((setting = config_lookup(cfg, ui_strings.self)) != NULL) { config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps); + + int time = 24; + if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) { + if (time == 12) { + snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p"); + snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]"); + } + } + + if ( config_setting_lookup_string(setting, ui_strings.timestamp_format, &str) ) { + snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", 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); + } + config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts); config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog); config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme); @@ -274,8 +297,6 @@ 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_other, &s->show_typing_other); config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg); - config_setting_lookup_int(setting, ui_strings.time_format, &s->time); - s->time = s->time == TIME_24 || s->time == TIME_12 ? s->time : TIME_24; /* Check defaults */ if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) { snprintf(s->line_join, sizeof(s->line_join), "%s", str); diff --git a/src/settings.h b/src/settings.h index 1cc9e13..cf9b628 100644 --- a/src/settings.h +++ b/src/settings.h @@ -32,8 +32,11 @@ struct user_settings { int autolog; /* boolean */ int alerts; /* boolean */ - int time; /* 12 or 24 */ + int timestamps; /* boolean */ + char timestamp_format[TIME_STR_SIZE]; + char log_timestamp_format[TIME_STR_SIZE]; + int colour_theme; /* boolean (0 for default toxic colours) */ int history_size; /* int between MIN_HISTORY and MAX_HISTORY */ int show_typing_self; /* boolean */ @@ -71,9 +74,6 @@ enum { AUTOLOG_OFF = 0, AUTOLOG_ON = 1, - TIME_24 = 24, - TIME_12 = 12, - TIMESTAMPS_OFF = 0, TIMESTAMPS_ON = 1, @@ -96,6 +96,8 @@ enum { #define LINE_QUIT "<--" #define LINE_ALERT "-!-" #define LINE_NORMAL "---" +#define TIMESTAMP_DEFAULT "%H:%M:%S" +#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]" int settings_load(struct user_settings *s, const char *patharg); #endif /* #define SETTINGS_H */ diff --git a/src/toxic.h b/src/toxic.h index 8c38498..fc6b512 100644 --- a/src/toxic.h +++ b/src/toxic.h @@ -47,7 +47,7 @@ #define MAX_CMDNAME_SIZE 64 #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 TIME_STR_SIZE 16 +#define TIME_STR_SIZE 32 /* ASCII key codes */ #define T_KEY_ESC 0x1B /* ESC key */ From 0c39e7b158ff74a2ef4ed62a72b5b7a3c878ac7f Mon Sep 17 00:00:00 2001 From: fr33domlover Date: Thu, 26 Feb 2015 23:51:20 +0200 Subject: [PATCH 2/3] Add support for auto-away based on screen attach/detach --- build/Makefile | 2 +- cfg/systems/Linux.mk | 2 +- doc/toxic.conf.5 | 10 ++ doc/toxic.conf.5.asc | 9 + misc/toxic.conf.example | 6 + src/global_commands.c | 11 +- src/settings.c | 17 ++ src/settings.h | 9 + src/term_mplex.c | 364 ++++++++++++++++++++++++++++++++++++++++ src/term_mplex.h | 32 ++++ src/toxic.c | 27 ++- src/toxic.h | 3 + 12 files changed, 486 insertions(+), 6 deletions(-) create mode 100644 src/term_mplex.c create mode 100644 src/term_mplex.h diff --git a/build/Makefile b/build/Makefile index 9131137..2ebc5db 100644 --- a/build/Makefile +++ b/build/Makefile @@ -14,7 +14,7 @@ LDFLAGS = $(USER_LDFLAGS) OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o notify.o OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o -OBJ += group_commands.o +OBJ += group_commands.o term_mplex.o # Check on wich system we are running UNAME_S = $(shell uname -s) diff --git a/cfg/systems/Linux.mk b/cfg/systems/Linux.mk index c8bf512..9a67614 100644 --- a/cfg/systems/Linux.mk +++ b/cfg/systems/Linux.mk @@ -1,4 +1,4 @@ # Specials options for linux systems CFLAGS += -LDFLAGS += -ldl -lresolv +LDFLAGS += -ldl -lresolv -lrt MANDIR = $(PREFIX)/share/man diff --git a/doc/toxic.conf.5 b/doc/toxic.conf.5 index 0ee128a..7adb194 100644 --- a/doc/toxic.conf.5 +++ b/doc/toxic.conf.5 @@ -137,6 +137,16 @@ Indicator for alert messages\&. .RS 4 Indicator for normal messages\&. .RE +.PP +\fBmplex_away\fR +.RS 4 +Set user status when attaching and detaching from GNU screen or tmux\&. true or false +.RE +.PP +\fBmplex_away_note\fR +.RS 4 +Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&. +.RE .RE .PP \fBaudio\fR diff --git a/doc/toxic.conf.5.asc b/doc/toxic.conf.5.asc index 5411bba..bfeb4fe 100644 --- a/doc/toxic.conf.5.asc +++ b/doc/toxic.conf.5.asc @@ -88,6 +88,15 @@ OPTIONS *line_normal*;; Indicator for normal messages. + *mplex_away*;; + Set user status when attaching and detaching from GNU screen or tmux. + true or false + + *mplex_away_note*;; + Status message to set when status is set to away due to screen/tmux + detach. When attaching, the status message is set back to the original + value. + *audio*:: Configuration related to audio devices. diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index ed2e24f..a4bf7fd 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -43,6 +43,12 @@ ui = { // Indicator for normal messages. line_normal="---"; + + // true to change status based on screen/tmux attach/detach, false to disable + mplex_away=true; + + // Status message to use when status set to away due to screen/tmux detach + mplex_away_note="Away from keyboard, be back soon!" }; audio = { diff --git a/src/global_commands.c b/src/global_commands.c index 93837b5..163db0c 100644 --- a/src/global_commands.c +++ b/src/global_commands.c @@ -528,12 +528,14 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ bool have_note = false; const char *errmsg; + lock_status (); + if (argc >= 2) { have_note = true; } else if (argc < 1) { errmsg = "Require a status. Statuses are: online, busy and away."; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); - return; + goto finish; } char status[MAX_STR_SIZE]; @@ -551,7 +553,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ else { errmsg = "Invalid status. Valid statuses are: online, busy and away."; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); - return; + goto finish; } tox_set_user_status(m, status_kind); @@ -560,7 +562,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ if (have_note) { if (argv[2][0] != '\"') { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); - return; + goto finish; } /* remove opening and closing quotes */ @@ -571,4 +573,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ prompt_update_statusmessage(prompt, m, msg); } + +finish: + unlock_status (); } diff --git a/src/settings.c b/src/settings.c index ca30f3e..3775e5b 100644 --- a/src/settings.c +++ b/src/settings.c @@ -62,6 +62,9 @@ static struct ui_strings { const char* line_quit; const char* line_alert; const char* line_normal; + + const char* mplex_away; + const char* mplex_away_note; } ui_strings = { "ui", "timestamps", @@ -79,6 +82,8 @@ static struct ui_strings { "line_quit", "line_alert", "line_normal", + "mplex_away", + "mplex_away_note", }; static void ui_defaults(struct user_settings* settings) @@ -99,6 +104,12 @@ static void ui_defaults(struct user_settings* settings) snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT); snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); + + settings->mplex_away = MPLEX_ON; + snprintf (settings->mplex_away_note, + sizeof (settings->mplex_away_note), + "%s", + MPLEX_AWAY_NOTE); } static const struct keys_strings { @@ -310,6 +321,12 @@ int settings_load(struct user_settings *s, const char *patharg) if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) { snprintf(s->line_normal, sizeof(s->line_normal), "%s", str); } + + config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away); + + if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) { + snprintf (s->mplex_away_note, sizeof (s->mplex_away_note), "%s", str); + } } /* paths */ diff --git a/src/settings.h b/src/settings.h index cf9b628..5403f0e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -25,6 +25,8 @@ #include +#include + /* Represents line_* hints max strlen */ #define LINE_HINT_MAX 3 @@ -63,6 +65,9 @@ struct user_settings { int key_peer_list_down; int key_toggle_peerlist; + int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */ + char mplex_away_note [TOX_MAX_STATUSMESSAGE_LENGTH]; + #ifdef AUDIO int audio_in_dev; int audio_out_dev; @@ -90,6 +95,9 @@ enum { SHOW_WELCOME_MSG_ON = 1, DFLT_HST_SIZE = 700, + + MPLEX_OFF = 0, + MPLEX_ON = 1, } settings_values; #define LINE_JOIN "-->" @@ -98,6 +106,7 @@ enum { #define LINE_NORMAL "---" #define TIMESTAMP_DEFAULT "%H:%M:%S" #define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]" +#define MPLEX_AWAY_NOTE "Detached from screen" int settings_load(struct user_settings *s, const char *patharg); #endif /* #define SETTINGS_H */ diff --git a/src/term_mplex.c b/src/term_mplex.c new file mode 100644 index 0000000..3c9b34f --- /dev/null +++ b/src/term_mplex.c @@ -0,0 +1,364 @@ +/* term_mplex.c + * + * + * Copyright (C) 2015 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 . + * + */ + +#include /* PATH_MAX */ +#include /* fgets, popen, pclose */ +#include /* malloc, realloc, free, getenv */ +#include /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "global_commands.h" +#include "windows.h" +#include "term_mplex.h" +#include "toxic.h" +#include "settings.h" + +extern struct ToxWindow *prompt; +extern struct user_settings *user_settings; +extern struct Winthread Winthread; + +#if defined(PATH_MAX) && PATH_MAX > 512 +#define BUFFER_SIZE PATH_MAX +#else +#define BUFFER_SIZE 512 +#endif + +#define PATH_SEP_S "/" +#define PATH_SEP_C '/' + +typedef enum +{ + MPLEX_NONE, + MPLEX_SCREEN, + MPLEX_TMUX, +} mplex_status; + +/* used for: + - storing screen socket name + - storing tmux session number in string form */ +static char mplex_data [BUFFER_SIZE]; + +static char buffer [BUFFER_SIZE]; + +static mplex_status mplex = MPLEX_NONE; +static TOX_USERSTATUS prev_status = TOX_USERSTATUS_NONE; +static char prev_note [TOX_MAX_STATUSMESSAGE_LENGTH] = ""; +static Tox *tox = NULL; + +static char *read_into_dyn_buffer (FILE *stream) +{ + const char *input_ptr = NULL; + char *dyn_buffer = NULL; + int dyn_buffer_size = 1; /* account for the \0 */ + + while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) + { + int length = dyn_buffer_size + strlen (input_ptr); + if (dyn_buffer) + dyn_buffer = (char*) realloc (dyn_buffer, length); + else + dyn_buffer = (char*) malloc (length); + strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr); + dyn_buffer_size = length; + } + + return dyn_buffer; +} + +static char *extract_socket_path (const char *info) +{ + const char *search_str = " Socket"; + const char *pos = strstr (info, search_str); + char *end = NULL; + char* path = NULL; + + if (!pos) + return NULL; + + pos += strlen (search_str); + pos = strchr (pos, PATH_SEP_C); + if (!pos) + return NULL; + + end = strchr (pos, '\n'); + if (!end) + return NULL; + + *end = '\0'; + end = strrchr (pos, '.'); + if (!end) + return NULL; + + path = (char*) malloc (end - pos + 1); + *end = '\0'; + return strcpy (path, pos); +} + +static int detect_gnu_screen () +{ + FILE *session_info_stream = NULL; + char *socket_name = NULL, *socket_path = NULL; + char *dyn_buffer = NULL; + + socket_name = getenv ("STY"); + if (!socket_name) + goto nomplex; + + session_info_stream = popen ("env LC_ALL=C screen -ls", "r"); + if (!session_info_stream) + goto nomplex; + + dyn_buffer = read_into_dyn_buffer (session_info_stream); + if (!dyn_buffer) + goto nomplex; + + pclose (session_info_stream); + session_info_stream = NULL; + + socket_path = extract_socket_path (dyn_buffer); + if (!socket_path) + goto nomplex; + + free (dyn_buffer); + dyn_buffer = NULL; + strcpy (mplex_data, socket_path); + strcat (mplex_data, PATH_SEP_S); + strcat (mplex_data, socket_name); + free (socket_path); + socket_path = NULL; + + mplex = MPLEX_SCREEN; + return 1; + +nomplex: + if (session_info_stream) + pclose (session_info_stream); + if (dyn_buffer) + free (dyn_buffer); + return 0; +} + +static int detect_tmux () +{ + char *tmux_env = getenv ("TMUX"), *pos; + if (!tmux_env) + return 0; + + /* find second separator */ + pos = strrchr (tmux_env, ','); + if (!pos) + return 0; + + /* store the session number string for later use */ + strcpy (mplex_data, pos + 1); + mplex = MPLEX_TMUX; + return 1; +} + +/* Checks whether a terminal multiplexer (mplex) is present, and finds + its unix socket. + + GNU screen and tmux are supported. + + Returns 1 if present, 0 otherwise. This value can be used to determine + whether an auto-away detection timer is needed. + */ +static int detect_mplex () +{ + /* try screen, and if fails try tmux */ + return detect_gnu_screen () || detect_tmux (); +} + +/* Detects gnu screen session attached/detached by examining permissions of + the session's unix socket. + */ +static int gnu_screen_is_detached () +{ + if (mplex != MPLEX_SCREEN) + return 0; + + struct stat sb; + if (stat (mplex_data, &sb) != 0) + return 0; + + /* execution permission (x) means attached */ + return ! (sb.st_mode & S_IXUSR); +} + +/* Detects tmux attached/detached by getting session data and finding the + current session's entry. An attached entry ends with "(attached)". Example: + + $ tmux list-sessions + 0: 1 windows (created Mon Mar 2 21:48:29 2015) [80x23] (attached) + 1: 2 windows (created Mon Mar 2 21:48:43 2015) [80x23] + + In this example, session 0 is attached and session 1 is detached. +*/ +static int tmux_is_detached () +{ + if (mplex != MPLEX_TMUX) + return 0; + + FILE *session_info_stream = NULL; + char *dyn_buffer = NULL, *search_str = NULL; + char *entry_pos, *nl_pos, *attached_pos; + const int numstr_len = strlen (mplex_data); + + session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r"); + if (!session_info_stream) + goto fail; + + dyn_buffer = read_into_dyn_buffer (session_info_stream); + if (!dyn_buffer) + goto fail; + + pclose (session_info_stream); + session_info_stream = NULL; + + /* prepare search string, for finding the current session's entry */ + search_str = (char*) malloc (numstr_len + 4); + search_str[0] = '\n'; + strcpy (search_str + 1, mplex_data); + strcat (search_str, ": "); + + /* do the search */ + if (strncmp (dyn_buffer, search_str + 1, numstr_len + 2) == 0) + entry_pos = dyn_buffer; + else + entry_pos = strstr (dyn_buffer, search_str); + + if (! entry_pos) + goto fail; + + /* find the next \n and look for the "(attached)" before it */ + nl_pos = strchr (entry_pos + 1, '\n'); + attached_pos = strstr (entry_pos + 1, "(attached)\n"); + + free (search_str); + search_str = NULL; + + free (dyn_buffer); + dyn_buffer = NULL; + + return attached_pos == NULL || attached_pos > nl_pos; + +fail: + if (session_info_stream) + pclose (session_info_stream); + if (dyn_buffer) + free (dyn_buffer); + if (search_str) + free (search_str); + return 0; +} + +/* Checks whether there is a terminal multiplexer present, but in detached + state. Returns 1 if detached, 0 if attached or if there is no terminal + multiplexer. + + If detect_mplex_socket() failed to find a mplex, there is no need to call + this function. If it did find one, this function can be used to periodically + sample its state and update away status according to attached/detached state + of the mplex. + */ +static int mplex_is_detached () +{ + return gnu_screen_is_detached () || tmux_is_detached (); +} + +static void mplex_timer_handler (union sigval param) +{ + int detached; + TOX_USERSTATUS current_status, new_status; + const char *new_note; + + if (mplex == MPLEX_NONE) + return; + + detached = mplex_is_detached (); + current_status = tox_get_self_user_status (tox); + + if (current_status == TOX_USERSTATUS_AWAY && !detached) + { + new_status = prev_status; + new_note = prev_note; + } + else + if (current_status != TOX_USERSTATUS_AWAY && detached) + { + prev_status = current_status; + new_status = TOX_USERSTATUS_AWAY; + tox_get_self_status_message (tox, + (uint8_t*) prev_note, + sizeof (prev_note)); + new_note = user_settings->mplex_away_note; + } + else + return; + + char argv[3][MAX_STR_SIZE]; + strcpy (argv[0], "/status"); + strcpy (argv[1], (new_status == TOX_USERSTATUS_AWAY ? "away" : + new_status == TOX_USERSTATUS_BUSY ? "busy" : "online")); + argv[2][0] = '\"'; + strcpy (argv[2] + 1, new_note); + strcat (argv[2], "\""); + pthread_mutex_lock (&Winthread.lock); + cmd_status (prompt->chatwin->history, prompt, tox, 2, argv); + pthread_mutex_unlock (&Winthread.lock); +} + +void init_mplex_away_timer (Tox *m) +{ + struct sigevent sev; + timer_t timer_id; + struct itimerspec its; + + if (! detect_mplex ()) + return; + + if (! user_settings->mplex_away) + return; + + sev.sigev_notify = SIGEV_THREAD; + sev.sigev_notify_function = mplex_timer_handler; + sev.sigev_notify_attributes = NULL; + + if (timer_create (CLOCK_REALTIME, &sev, &timer_id) == -1) + return; + + its.it_interval.tv_sec = 5; + its.it_interval.tv_nsec = 0; + its.it_value = its.it_interval; + + timer_settime (timer_id, 0, &its, NULL); + tox = m; +} diff --git a/src/term_mplex.h b/src/term_mplex.h new file mode 100644 index 0000000..bccf5c9 --- /dev/null +++ b/src/term_mplex.h @@ -0,0 +1,32 @@ +/* term_mplex.h + * + * + * Copyright (C) 2015 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 . + * + */ + +#ifndef TERM_MPLEX_H +#define TERM_MPLEX_H + +/* Checks if Toxic runs inside a terminal multiplexer (GNU screen or tmux). If + yes, it initializes a timer which periodically checks the attached/detached + state of the terminal and updates away status accordingly. + */ +void init_mplex_away_timer (Tox *m); + +#endif /* #define TERM_MPLEX_H */ diff --git a/src/toxic.c b/src/toxic.c index 71035d3..1d7dc10 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -57,6 +57,7 @@ #include "device.h" #include "message_queue.h" #include "execute.h" +#include "term_mplex.h" #ifdef X11 #include "xtra.h" @@ -84,6 +85,23 @@ struct audio_thread audio_thread; struct arg_opts arg_opts; struct user_settings *user_settings = NULL; +/* mutex for access to status data, for sync between: + - user command /status from ncurses thread + - auto-away POSIX timer, which runs from a separate thread + after init, should be accessed only by cmd_status() + */ +static pthread_mutex_t status_lock; + +void lock_status () +{ + pthread_mutex_lock (&status_lock); +} + +void unlock_status () +{ + pthread_mutex_unlock (&status_lock); +} + #define MIN_PASSWORD_LEN 6 #define MAX_PASSWORD_LEN 64 @@ -1071,7 +1089,11 @@ int main(int argc, char *argv[]) /* 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); - + + /* status access mutex */ + if (pthread_mutex_init (&status_lock, NULL) != 0) + exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); + #ifdef AUDIO av = init_audio(prompt, m); @@ -1109,6 +1131,9 @@ int main(int argc, char *argv[]) snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); + /* screen/tmux auto-away timer */ + init_mplex_away_timer (m); + uint64_t last_save = (uint64_t) time(NULL); uint64_t looptimer = last_save; useconds_t msleepval = 40000; diff --git a/src/toxic.h b/src/toxic.h index fc6b512..efe2cd3 100644 --- a/src/toxic.h +++ b/src/toxic.h @@ -90,6 +90,9 @@ typedef enum _FATAL_ERRS { Uncomment if necessary */ /* #define URXVT_FIX */ +void lock_status (); +void unlock_status (); + void exit_toxic_success(Tox *m); void exit_toxic_err(const char *errmsg, int errcode); From 442d9e22b45d1c7057cada878f5c30771edff6a6 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Wed, 11 Mar 2015 18:38:07 -0400 Subject: [PATCH 3/3] use pthread for mplex detach polling for better portability --- src/global_commands.c | 3 +- src/term_mplex.c | 79 ++++++++++++++++++++++++++++--------------- src/term_mplex.h | 5 ++- src/toxic.c | 60 +++++++++++--------------------- 4 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/global_commands.c b/src/global_commands.c index 163db0c..89e753c 100644 --- a/src/global_commands.c +++ b/src/global_commands.c @@ -34,6 +34,7 @@ #include "groupchat.h" #include "prompt.h" #include "help.h" +#include "term_mplex.h" extern char *DATA_FILE; extern ToxWindow *prompt; @@ -524,7 +525,7 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv } void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) -{ +{ bool have_note = false; const char *errmsg; diff --git a/src/term_mplex.c b/src/term_mplex.c index 3c9b34f..9370be2 100644 --- a/src/term_mplex.c +++ b/src/term_mplex.c @@ -23,11 +23,9 @@ #include /* PATH_MAX */ #include /* fgets, popen, pclose */ #include /* malloc, realloc, free, getenv */ -#include /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp - */ +#include /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp */ #include -#include #include #include #include @@ -71,7 +69,24 @@ static char buffer [BUFFER_SIZE]; static mplex_status mplex = MPLEX_NONE; static TOX_USERSTATUS prev_status = TOX_USERSTATUS_NONE; static char prev_note [TOX_MAX_STATUSMESSAGE_LENGTH] = ""; -static Tox *tox = NULL; + +/* mutex for access to status data, for sync between: + - user command /status from ncurses thread + - auto-away POSIX timer, which runs from a separate thread + after init, should be accessed only by cmd_status() + */ +pthread_mutex_t status_lock; +pthread_t mplex_tid; + +void lock_status () +{ + pthread_mutex_lock (&status_lock); +} + +void unlock_status () +{ + pthread_mutex_unlock (&status_lock); +} static char *read_into_dyn_buffer (FILE *stream) { @@ -294,7 +309,7 @@ static int mplex_is_detached () return gnu_screen_is_detached () || tmux_is_detached (); } -static void mplex_timer_handler (union sigval param) +static void mplex_timer_handler (Tox *m) { int detached; TOX_USERSTATUS current_status, new_status; @@ -304,7 +319,10 @@ static void mplex_timer_handler (union sigval param) return; detached = mplex_is_detached (); - current_status = tox_get_self_user_status (tox); + + pthread_mutex_lock (&Winthread.lock); + current_status = tox_get_self_user_status (m); + pthread_mutex_unlock (&Winthread.lock); if (current_status == TOX_USERSTATUS_AWAY && !detached) { @@ -316,9 +334,9 @@ static void mplex_timer_handler (union sigval param) { prev_status = current_status; new_status = TOX_USERSTATUS_AWAY; - tox_get_self_status_message (tox, - (uint8_t*) prev_note, - sizeof (prev_note)); + pthread_mutex_lock (&Winthread.lock); + tox_get_self_status_message (m, (uint8_t*) prev_note, sizeof (prev_note)); + pthread_mutex_unlock (&Winthread.lock); new_note = user_settings->mplex_away_note; } else @@ -332,33 +350,38 @@ static void mplex_timer_handler (union sigval param) strcpy (argv[2] + 1, new_note); strcat (argv[2], "\""); pthread_mutex_lock (&Winthread.lock); - cmd_status (prompt->chatwin->history, prompt, tox, 2, argv); + cmd_status (prompt->chatwin->history, prompt, m, 2, argv); pthread_mutex_unlock (&Winthread.lock); } -void init_mplex_away_timer (Tox *m) -{ - struct sigevent sev; - timer_t timer_id; - struct itimerspec its; +/* Time in seconds between calls to mplex_timer_handler */ +#define MPLEX_TIMER_INTERVAL 5 +void *mplex_timer_thread(void *data) +{ + Tox *m = (Tox *) data; + + while (true) { + sleep(MPLEX_TIMER_INTERVAL); + mplex_timer_handler(m); + } +} + +int init_mplex_away_timer (Tox *m) +{ if (! detect_mplex ()) - return; + return 0; if (! user_settings->mplex_away) - return; + return 0; - sev.sigev_notify = SIGEV_THREAD; - sev.sigev_notify_function = mplex_timer_handler; - sev.sigev_notify_attributes = NULL; + /* status access mutex */ + if (pthread_mutex_init (&status_lock, NULL) != 0) + return -1; - if (timer_create (CLOCK_REALTIME, &sev, &timer_id) == -1) - return; + if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0) + return -1; - its.it_interval.tv_sec = 5; - its.it_interval.tv_nsec = 0; - its.it_value = its.it_interval; - - timer_settime (timer_id, 0, &its, NULL); - tox = m; + return 0; } + diff --git a/src/term_mplex.h b/src/term_mplex.h index bccf5c9..ce9387e 100644 --- a/src/term_mplex.h +++ b/src/term_mplex.h @@ -27,6 +27,9 @@ yes, it initializes a timer which periodically checks the attached/detached state of the terminal and updates away status accordingly. */ -void init_mplex_away_timer (Tox *m); +int init_mplex_away_timer (Tox *m); + +void lock_status (); +void unlock_status (); #endif /* #define TERM_MPLEX_H */ diff --git a/src/toxic.c b/src/toxic.c index 1d7dc10..eec9cc6 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -85,23 +85,6 @@ struct audio_thread audio_thread; struct arg_opts arg_opts; struct user_settings *user_settings = NULL; -/* mutex for access to status data, for sync between: - - user command /status from ncurses thread - - auto-away POSIX timer, which runs from a separate thread - after init, should be accessed only by cmd_status() - */ -static pthread_mutex_t status_lock; - -void lock_status () -{ - pthread_mutex_lock (&status_lock); -} - -void unlock_status () -{ - pthread_mutex_unlock (&status_lock); -} - #define MIN_PASSWORD_LEN 6 #define MAX_PASSWORD_LEN 64 @@ -154,14 +137,14 @@ void exit_toxic_success(Tox *m) tox_kill(m); endwin(); - + #ifdef X11 /* We have to terminate xtra last coz reasons * Please don't call this anywhere else coz trust me */ terminate_xtra(); #endif /* X11 */ - + exit(EXIT_SUCCESS); } @@ -536,7 +519,7 @@ static void first_time_encrypt(const char *msg) system("clear"); printf("%s ", msg); - if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n") + if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n") || !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n")) break; @@ -1061,41 +1044,37 @@ int main(int argc, char *argv[]) if (init_xtra(DnD_callback) == -1) queue_init_message("X failed to initialize"); #endif - + Tox *m = init_tox(); - + if (m == NULL) - exit_toxic_err("failed in main", FATALERR_NETWORKINIT); - + exit_toxic_err("failed in main", FATALERR_NETWORKINIT); + if (!arg_opts.ignore_data_file) { if (arg_opts.encrypt_data && !datafile_exists) arg_opts.encrypt_data = 0; - + load_data(m, DATA_FILE); - + } - + init_term(); prompt = init_windows(m); prompt_init_statusbar(prompt, m); - + /* thread for ncurses stuff */ if (pthread_mutex_init(&Winthread.lock, NULL) != 0) 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); - /* status access mutex */ - if (pthread_mutex_init (&status_lock, NULL) != 0) - exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); - #ifdef AUDIO - + av = init_audio(prompt, m); /* audio thread */ @@ -1110,11 +1089,11 @@ int main(int argc, char *argv[]) queue_init_message("Failed to init audio devices"); #endif /* AUDIO */ - + init_notify(60, 3000); const char *msg; - + if (config_err) { msg = "Unable to determine configuration directory. Defaulting to 'data' for data file..."; queue_init_message("%s", msg); @@ -1123,6 +1102,10 @@ int main(int argc, char *argv[]) if (settings_err == -1) queue_init_message("Failed to load user settings"); + /* screen/tmux auto-away timer */ + if (init_mplex_away_timer (m) == -1) + queue_init_message("Failed to init mplex auto-away."); + print_init_messages(prompt); cleanup_init_messages(); @@ -1131,9 +1114,6 @@ int main(int argc, char *argv[]) snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); - /* screen/tmux auto-away timer */ - init_mplex_away_timer (m); - uint64_t last_save = (uint64_t) time(NULL); uint64_t looptimer = last_save; useconds_t msleepval = 40000;