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);