1
0
mirror of https://github.com/Tha14/toxic.git synced 2024-12-22 22:33:25 +01:00

Add support for auto-away based on screen attach/detach

This commit is contained in:
fr33domlover 2015-02-26 23:51:20 +02:00
parent 3e3f2614b5
commit 0c39e7b158
12 changed files with 486 additions and 6 deletions

View File

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

View File

@ -1,4 +1,4 @@
# Specials options for linux systems
CFLAGS +=
LDFLAGS += -ldl -lresolv
LDFLAGS += -ldl -lresolv -lrt
MANDIR = $(PREFIX)/share/man

View File

@ -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

View File

@ -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.

View File

@ -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 = {

View File

@ -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 ();
}

View File

@ -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 */

View File

@ -25,6 +25,8 @@
#include <limits.h>
#include <tox/tox.h>
/* 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 */

364
src/term_mplex.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include <limits.h> /* PATH_MAX */
#include <stdio.h> /* fgets, popen, pclose */
#include <stdlib.h> /* malloc, realloc, free, getenv */
#include <string.h> /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp
*/
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <tox/tox.h>
#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;
}

32
src/term_mplex.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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 */

View File

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

View File

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