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 fa30e86..7adb194 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
@@ -125,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 4643405..bfeb4fe 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
@@ -79,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 7b8e352..a4bf7fd 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;
@@ -40,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/friendlist.c b/src/friendlist.c
index 2540d8d..0fb475a 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/global_commands.c b/src/global_commands.c
index 6a43e85..6d762b9 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;
@@ -595,12 +596,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];
@@ -618,7 +621,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;
}
if (tox_set_user_status(m, status_kind) == -1) {
@@ -632,7 +635,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 */
@@ -643,4 +646,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/line_info.c b/src/line_info.c
index ab2d3c8..06b05e4 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 ee9ca0f..736e495 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..3775e5b 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;
@@ -60,13 +62,18 @@ 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",
+ "time_format",
+ "timestamp_format",
+ "log_timestamp_format",
"alerts",
"native_colors",
"autolog",
- "time_format",
"history_size",
"show_typing_self",
"show_typing_other",
@@ -75,12 +82,16 @@ static struct ui_strings {
"line_quit",
"line_alert",
"line_normal",
+ "mplex_away",
+ "mplex_away_note",
};
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;
@@ -93,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 {
@@ -267,6 +284,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 +308,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);
@@ -289,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 1cc9e13..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
@@ -32,8 +34,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 */
@@ -60,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;
@@ -71,9 +79,6 @@ enum {
AUTOLOG_OFF = 0,
AUTOLOG_ON = 1,
- TIME_24 = 24,
- TIME_12 = 12,
-
TIMESTAMPS_OFF = 0,
TIMESTAMPS_ON = 1,
@@ -90,12 +95,18 @@ enum {
SHOW_WELCOME_MSG_ON = 1,
DFLT_HST_SIZE = 700,
+
+ MPLEX_OFF = 0,
+ MPLEX_ON = 1,
} settings_values;
#define LINE_JOIN "-->"
#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]"
+#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..9370be2
--- /dev/null
+++ b/src/term_mplex.c
@@ -0,0 +1,387 @@
+/* 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 "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] = "";
+
+/* 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)
+{
+ 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 (Tox *m)
+{
+ int detached;
+ TOX_USERSTATUS current_status, new_status;
+ const char *new_note;
+
+ if (mplex == MPLEX_NONE)
+ return;
+
+ detached = mplex_is_detached ();
+
+ 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)
+ {
+ 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;
+ 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
+ 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, m, 2, argv);
+ pthread_mutex_unlock (&Winthread.lock);
+}
+
+/* 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 0;
+
+ if (! user_settings->mplex_away)
+ return 0;
+
+ /* status access mutex */
+ if (pthread_mutex_init (&status_lock, NULL) != 0)
+ return -1;
+
+ if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0)
+ return -1;
+
+ return 0;
+}
+
diff --git a/src/term_mplex.h b/src/term_mplex.h
new file mode 100644
index 0000000..ce9387e
--- /dev/null
+++ b/src/term_mplex.h
@@ -0,0 +1,35 @@
+/* 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.
+ */
+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 0318958..203d94f 100644
--- a/src/toxic.c
+++ b/src/toxic.c
@@ -58,6 +58,7 @@
#include "device.h"
#include "message_queue.h"
#include "execute.h"
+#include "term_mplex.h"
#ifdef X11
#include "xtra.h"
@@ -1121,6 +1122,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.");
+
load_groups(m);
print_init_messages(prompt);
cleanup_init_messages();
diff --git a/src/toxic.h b/src/toxic.h
index 551ef29..0a1ef8f 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 */
@@ -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);