$ git clone https://tcclient.ion.nu/tc_client.git
commit 48af5473dd52e2d641e68859b8b816d17b582355
Author: Alicia <...>
Date: Tue Apr 7 06:49:01 2015 +0200
Version 0.25
diff --git a/ChangeLog b/ChangeLog
index d5c1eaa..317ada6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+0.25:
+Handle short reads in the RTMP headers too.
+Added the option -c/--color <number> to pick color instead of getting a random color at startup (contributed by Pamela)
+cursedchat: use default colors (for background in particular)
+cursedchat: added scrolling.
+modbot: fixed singular form of 'videos' in !queue response.
+modbot: update 'requested' (for !requestedby) also when a video is played manually.
+camviewer: added basic mic code (mixing is still missing so it is disabled by default, to enable, configure with environment variable ENABLE_MIC set to 1)
0.24:
Fixed endianness of RTMP set-chunk-size handling.
modbot: fixed a bug where '!wrongrequest' would claim no request was found when (and only when) the request was last in queue.
diff --git a/Makefile b/Makefile
index 9735d2d..0f7e575 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-VERSION=0.24
+VERSION=0.25
CFLAGS=-g3 -Wall $(shell curl-config --cflags)
LIBS=-g3 $(shell curl-config --libs)
ifneq ($(wildcard config.mk),)
@@ -16,6 +16,11 @@ ifdef AVUTIL_LIBS
ifdef SWSCALE_LIBS
UTILS+=camviewer
CFLAGS+=$(GTK_CFLAGS) $(AVCODEC_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
+ ifdef SWRESAMPLE_LIBS
+ ifdef AO_LIBS
+ CFLAGS+=-DHAVE_SOUND $(SWRESAMPLE_CFLAGS) $(AO_CFLAGS)
+ endif
+ endif
endif
endif
endif
@@ -39,7 +44,7 @@ modbot: $(MODBOT_OBJ)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
camviewer: $(CAMVIEWER_OBJ)
- $(CC) $(LDFLAGS) $^ $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) -o $@
+ $(CC) $(LDFLAGS) $^ $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(SWRESAMPLE_LIBS) $(AO_LIBS) -o $@
cursedchat: $(CURSEDCHAT_OBJ)
$(CC) $(LDFLAGS) $^ $(LIBS) $(READLINE_LIBS) $(CURSES_LIBS) -o $@
diff --git a/client.c b/client.c
index 9cd746d..3a3b5fa 100644
--- a/client.c
+++ b/client.c
@@ -2,6 +2,7 @@
tc_client, a simple non-flash client for tinychat(.com)
Copyright (C) 2014-2015 alicia@ion.nu
Copyright (C) 2014-2015 Jade Lea
+ Copyright (C) 2015 Pamela Hiatt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -210,6 +211,7 @@ void usage(const char* me)
"-h, --help Show this help text and exit\n"
"-u, --user <user> Username of tinychat account to use.\n"
"-p, --pass <pass> Password of tinychat account to use.\n"
+ "-c, --color <value> Color to use in chat.\n"
,me);
}
@@ -238,6 +240,11 @@ int main(int argc, char** argv)
++i;
account_pass=strdup(argv[i]);
}
+ else if(!strcmp(argv[i], "-c")||!strcmp(argv[i], "--color"))
+ {
+ ++i;
+ currentcolor=atoi(argv[i]);
+ }
else if(!channel){channel=argv[i];}
else if(!nickname){nickname=strdup(argv[i]);}
else if(!password){password=argv[i];}
@@ -245,7 +252,7 @@ int main(int argc, char** argv)
// Check for required arguments
if(!channel||!nickname){usage(argv[0]); return 1;}
char badchar;
- if((badchar=checknick(argv[2])))
+ if((badchar=checknick(nickname)))
{
printf("'%c' is not allowed in nicknames.\n", badchar);
return 1;
@@ -293,7 +300,10 @@ int main(int argc, char** argv)
// RTMP handshake
unsigned char handshake[1536];
read(random, handshake, 1536);
- read(random, ¤tcolor, sizeof(currentcolor));
+ if(currentcolor>=COLORCOUNT)
+ {
+ read(random, ¤tcolor, sizeof(currentcolor));
+ }
close(random);
write(sock, "\x03", 1); // Send 0x03 and 1536 bytes of random junk
write(sock, handshake, 1536);
@@ -415,9 +425,9 @@ int main(int argc, char** argv)
if(!strcmp(&buf[7], "off")){showcolor=0; continue;}
if(!strcmp(&buf[7], "on")){showcolor=1; continue;}
currentcolor=atoi(&buf[7]);
- printf("\x1b[%smChanged color\x1b[0m\n", termcolors[currentcolor%16]);
+ printf("\x1b[%smChanged color\x1b[0m\n", termcolors[currentcolor%COLORCOUNT]);
}else{ // No color specified, state our current color
- printf("\x1b[%smCurrent color: %i\x1b[0m\n", termcolors[currentcolor%16], currentcolor%16);
+ printf("\x1b[%smCurrent color: %i\x1b[0m\n", termcolors[currentcolor%COLORCOUNT], currentcolor%COLORCOUNT);
}
fflush(stdout);
continue;
@@ -425,7 +435,7 @@ int main(int argc, char** argv)
else if(!strcmp(buf, "/colors"))
{
int i;
- for(i=0; i<16; ++i)
+ for(i=0; i<COLORCOUNT; ++i)
{
printf("\x1b[%smColor %i\x1b[0m\n", termcolors[i], i);
}
@@ -553,7 +563,7 @@ int main(int argc, char** argv)
amfnum(&amf, 0);
amfnull(&amf);
amfstring(&amf, msg);
- amfstring(&amf, colors[currentcolor%16]);
+ amfstring(&amf, colors[currentcolor%COLORCOUNT]);
// For PMs, add a string like "n<numeric ID>-<nick>" to make the server only send it to the intended recipient
if(privfield)
{
diff --git a/colors.c b/colors.c
index 6812d67..28969eb 100644
--- a/colors.c
+++ b/colors.c
@@ -1,6 +1,7 @@
/*
tc_client, a simple non-flash client for tinychat(.com)
- Copyright (C) 2014 alicia@ion.nu
+ Copyright (C) 2014-2015 alicia@ion.nu
+ Copyright (C) 2015 Pamela Hiatt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -18,7 +19,7 @@
#include "colors.h"
// Sorted like rainbows
-const char* colors[]={ // The 16 colors accepted by the flash client
+const char* colors[COLORCOUNT]={ // The 16 colors accepted by the flash client
"#821615,en",
"#c53332,en",
"#a08f23,en",
@@ -37,7 +38,7 @@ const char* colors[]={ // The 16 colors accepted by the flash client
"#b9807f,en"
};
-const char* termcolors[]={ // Equivalent color codes for ANSI escape sequences
+const char* termcolors[COLORCOUNT]={ // Equivalent color codes for ANSI escape sequences
"31",
"31;1",
"33",
@@ -56,12 +57,12 @@ const char* termcolors[]={ // Equivalent color codes for ANSI escape sequences
"35;1"
};
-unsigned int currentcolor;
+unsigned int currentcolor=COLORCOUNT;
const char* resolvecolor(const char* tc_color)
{
int i;
- for(i=0; i<16; ++i)
+ for(i=0; i<COLORCOUNT; ++i)
{
if(!strcmp(colors[i], tc_color)){return termcolors[i];}
}
diff --git a/colors.h b/colors.h
index a57cee2..baa5d9b 100644
--- a/colors.h
+++ b/colors.h
@@ -1,6 +1,6 @@
/*
tc_client, a simple non-flash client for tinychat(.com)
- Copyright (C) 2014 alicia@ion.nu
+ Copyright (C) 2014-2015 alicia@ion.nu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -14,6 +14,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define COLORCOUNT 16
extern const char* colors[];
extern const char* termcolors[];
extern unsigned int currentcolor;
diff --git a/configure b/configure
index 4d62ba9..28bb16d 100755
--- a/configure
+++ b/configure
@@ -87,6 +87,29 @@ else
echo no
fi
+# TODO: Figure out how to mix sound sources so that this doesn't sound horrible with more than one person on mic, having it disabled by default until then
+if [ "x${ENABLE_MIC}" != "x" ]; then
+printf 'Checking for libswresample... '
+libs="`pkg-config --libs libswresample 2> /dev/null`"
+if [ "x$libs" != "x" ]; then
+ echo "SWRESAMPLE_LIBS=${libs}" >> config.mk
+ echo "SWRESAMPLE_CFLAGS=`pkg-config --cflags libswresample`" >> config.mk
+ echo yes
+else
+ echo no
+fi
+
+printf 'Checking for libao... '
+libs="`pkg-config --libs ao 2> /dev/null`"
+if [ "x$libs" != "x" ]; then
+ echo "AO_LIBS=${libs}" >> config.mk
+ echo "AO_CFLAGS=`pkg-config --cflags ao`" >> config.mk
+ echo yes
+else
+ echo no
+fi
+fi
+
printf 'Checking for ncurses... '
libs="`ncursesw5-config --libs 2> /dev/null || ncurses5-config --libs 2> /dev/null`"
if [ "x$libs" != "x" ]; then
diff --git a/rtmp.c b/rtmp.c
index 4cb900d..035ff7a 100644
--- a/rtmp.c
+++ b/rtmp.c
@@ -35,6 +35,19 @@ struct chunk* chunks=0;
unsigned int chunkcount=0;
unsigned int chunksize_in=128;
+size_t fullread(int fd, void* buf, size_t len)
+{
+ size_t remaining=len;
+ while(remaining>0)
+ {
+ size_t r=read(fd, buf, remaining);
+ if(r<1){return 0;}
+ remaining-=r;
+ buf+=r;
+ }
+ return len;
+}
+
struct chunk* chunk_get(unsigned int id)
{
unsigned int i;
@@ -58,40 +71,40 @@ char rtmp_get(int sock, struct rtmp* rtmp)
{
// Header format and chunk ID
unsigned int x=0;
- if(read(sock, &x, 1)<1){return 0;}
+ if(fullread(sock, &x, 1)<1){return 0;}
unsigned int chunkid=x&0x3f;
unsigned int fmt=(x&0xc0)>>6;
struct chunk* chunk=chunk_get(chunkid);
// Handle extended stream IDs
if(chunkid<2) // (0=1 extra byte, 1=2 extra bytes)
{
- read(sock, &x, chunkid+1);
+ fullread(sock, &x, chunkid+1);
chunkid=64+x;
}
if(fmt<3)
{
// Timestamp
- read(sock, &x, 3);
+ fullread(sock, &x, 3);
chunk->timestamp=x;
if(fmt<2)
{
// Length
x=0;
- read(sock, ((void*)&x)+1, 3);
+ fullread(sock, ((void*)&x)+1, 3);
chunk->length=be32(x);
// Type
- read(sock, &chunk->type, sizeof(chunk->type));
+ fullread(sock, &chunk->type, sizeof(chunk->type));
if(fmt<1)
{
// Message ID
- read(sock, &chunk->streamid, sizeof(chunk->streamid));
+ fullread(sock, &chunk->streamid, sizeof(chunk->streamid));
}
}
}
// Extended timestamp
if(chunk->timestamp==0xffffff)
{
- read(sock, &x, sizeof(x));
+ fullread(sock, &x, sizeof(x));
chunk->timestamp=be32(x);
}
@@ -101,14 +114,7 @@ char rtmp_get(int sock, struct rtmp* rtmp)
chunk->pos=0;
}
unsigned int rsize=((chunk->length-chunk->pos>=chunksize_in)?chunksize_in:(chunk->length-chunk->pos));
- while(rsize>0)
- {
- size_t r=read(sock, chunk->buf+chunk->pos, rsize);
- if(r<1){return 0;}
- rsize-=r;
- chunk->pos+=r;
-// if(rsize){printf("Got a short read, %u remaining\n", rsize);}
- }
+ chunk->pos+=fullread(sock, chunk->buf+chunk->pos, rsize);
if(chunk->pos==chunk->length)
{
if(chunk->type==RTMP_SET_PACKET_SIZE)
diff --git a/utilities/camviewer/camviewer.c b/utilities/camviewer/camviewer.c
index a1a8a6b..1f9330c 100644
--- a/utilities/camviewer/camviewer.c
+++ b/utilities/camviewer/camviewer.c
@@ -18,6 +18,10 @@
#include <fcntl.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
+#ifdef HAVE_SOUND
+#include <libswresample/swresample.h>
+#include <ao/ao.h>
+#endif
#include <gtk/gtk.h>
#if GTK_MAJOR_VERSION==2
@@ -39,7 +43,8 @@ struct camera
AVFrame* frame;
AVFrame* dstframe;
GtkWidget* cam;
- AVCodecContext* ctx;
+ AVCodecContext* vctx;
+ AVCodecContext* actx;
char* id;
char* nick;
GtkWidget* box; // holds label and cam
@@ -50,7 +55,11 @@ struct viddata
struct camera* cams;
unsigned int camcount;
GtkWidget* box;
- AVCodec* decoder;
+ AVCodec* vdecoder;
+ AVCodec* adecoder;
+#ifdef HAVE_SOUND
+ int audiopipe;
+#endif
};
int tc_client[2];
@@ -106,8 +115,12 @@ gboolean handledata(GIOChannel* channel, GIOCondition condition, gpointer datap)
cam->dstframe=av_frame_alloc();
cam->nick=strdup(nick);
cam->id=strdup(id);
- cam->ctx=avcodec_alloc_context3(data->decoder);
- avcodec_open2(cam->ctx, data->decoder, 0);
+ cam->vctx=avcodec_alloc_context3(data->vdecoder);
+ avcodec_open2(cam->vctx, data->vdecoder, 0);
+#ifdef HAVE_SOUND
+ cam->actx=avcodec_alloc_context3(data->adecoder);
+ avcodec_open2(cam->actx, data->adecoder, 0);
+#endif
cam->cam=gtk_image_new();
cam->box=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(cam->box), cam->cam, 0, 0, 0);
@@ -124,7 +137,10 @@ gboolean handledata(GIOChannel* channel, GIOCondition condition, gpointer datap)
{
gtk_widget_destroy(data->cams[i].box);
av_frame_free(&data->cams[i].frame);
- avcodec_free_context(&data->cams[i].ctx);
+ avcodec_free_context(&data->cams[i].vctx);
+#ifdef HAVE_SOUND
+ avcodec_free_context(&data->cams[i].actx);
+#endif
free(data->cams[i].id);
free(data->cams[i].nick);
--data->camcount;
@@ -138,20 +154,42 @@ gboolean handledata(GIOChannel* channel, GIOCondition condition, gpointer datap)
{
char* sizestr=strchr(&buf[7], ' ');
if(!sizestr){return 1;}
+ sizestr[0]=0;
unsigned int size=strtoul(&sizestr[1], 0, 0);
if(!size){return 1;}
unsigned char frameinfo;
read(tc_client[0], &frameinfo, 1);
--size; // For the byte we read above
- unsigned char buf[size];
+ AVPacket pkt;
+ av_init_packet(&pkt);
+ unsigned char databuf[size];
+ pkt.data=databuf;
+ pkt.size=size;
unsigned int pos=0;
while(pos<size)
{
- pos+=read(tc_client[0], &buf[pos], size-pos);
+ pos+=read(tc_client[0], pkt.data+pos, size-pos);
}
-// TODO: decode and send to a sound lib (libao)
+#ifdef HAVE_SOUND
+ // Find the camera representation for the given ID (for decoder context)
+ struct camera* cam=0;
+ for(i=0; i<data->camcount; ++i)
+ {
+ if(!strcmp(data->cams[i].id, &buf[7])){cam=&data->cams[i]; break;}
+ }
+ if(!cam){printf("No cam found with ID '%s'\n", &buf[7]); free(pkt.data); return 1;}
+ int gotframe;
+ avcodec_decode_audio4(cam->actx, cam->frame, &gotframe, &pkt);
+ if(!gotframe){return 1;}
+ SwrContext* swrctx=swr_alloc_set_opts(0, 1, AV_SAMPLE_FMT_S16, 22050, 1, cam->frame->format, 11025, 0, 0); // TODO: any way to get the sample rate from the frame/decoder? cam->frame->sample_rate seems to be 0
+ swr_init(swrctx);
+ int outlen=swr_convert(swrctx, cam->frame->data, cam->frame->nb_samples, (const uint8_t**)cam->frame->data, cam->frame->nb_samples);
+ swr_free(&swrctx);
+ write(data->audiopipe, cam->frame->data[0], outlen);
+#endif
+ return 1;
}
- if(strncmp(buf, "Video: ", 7)){printf("Got '%s'\n", buf); return 1;} // Ignore anything else that isn't video
+ if(strncmp(buf, "Video: ", 7)){printf("Got '%s'\n", buf); fflush(stdout); return 1;} // Ignore anything else that isn't video
char* sizestr=strchr(&buf[7], ' ');
if(!sizestr){return 1;}
sizestr[0]=0;
@@ -182,7 +220,7 @@ gboolean handledata(GIOChannel* channel, GIOCondition condition, gpointer datap)
if(!cam){printf("No cam found with ID '%s'\n", &buf[7]); free(pkt.data); return 1;}
pkt.size=size;
int gotframe;
- avcodec_decode_video2(cam->ctx, cam->frame, &gotframe, &pkt);
+ avcodec_decode_video2(cam->vctx, cam->frame, &gotframe, &pkt);
if(!gotframe){return 1;}
// Convert to RGB24 format
@@ -203,11 +241,48 @@ gboolean handledata(GIOChannel* channel, GIOCondition condition, gpointer datap)
return 1;
}
+#ifdef HAVE_SOUND
+void audiothread(int fd)
+{
+ ao_initialize();
+ ao_sample_format samplefmt;
+ samplefmt.bits=16;
+ samplefmt.rate=22050;
+ samplefmt.channels=1;
+ samplefmt.byte_format=AO_FMT_NATIVE; // I'm guessing libavcodec decodes it to native
+ samplefmt.matrix=0;
+ ao_option clientname={.key="client_name", .value="tc_client/camviewer", .next=0};
+ ao_device* dev=ao_open_live(ao_default_driver_id(), &samplefmt, &clientname);
+// TODO: mix sounds, somehow..
+ char buf[2048];
+ size_t len;
+ while((len=read(fd, buf, 2048))>0)
+ {
+ ao_play(dev, buf, len);
+ }
+ ao_close(dev);
+}
+#endif
+
int main(int argc, char** argv)
{
- struct viddata data={0,0,0,0};
+ struct viddata data={0,0,0,0,0};
avcodec_register_all();
- data.decoder=avcodec_find_decoder(AV_CODEC_ID_FLV1);
+ data.vdecoder=avcodec_find_decoder(AV_CODEC_ID_FLV1);
+ data.adecoder=avcodec_find_decoder(AV_CODEC_ID_NELLYMOSER);
+
+#ifdef HAVE_SOUND
+ int audiopipe[2];
+ pipe(audiopipe);
+ data.audiopipe=audiopipe[1];
+ if(!fork())
+ {
+ close(audiopipe[1]);
+ audiothread(audiopipe[0]);
+ _exit(0);
+ }
+ close(audiopipe[0]);
+#endif
gtk_init(&argc, &argv);
GtkWidget* w=gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -234,14 +309,17 @@ int main(int argc, char** argv)
unsigned int channel_id=g_io_add_watch(tcchannel, G_IO_IN, handledata, &data);
gtk_main();
-
+
g_source_remove(channel_id);
g_io_channel_shutdown(tcchannel, 0, 0);
unsigned int i;
for(i=0; i<data.camcount; ++i)
{
av_frame_free(&data.cams[i].frame);
- avcodec_free_context(&data.cams[i].ctx);
+ avcodec_free_context(&data.cams[i].vctx);
+#ifdef HAVE_SOUND
+ avcodec_free_context(&data.cams[i].actx);
+#endif
free(data.cams[i].id);
free(data.cams[i].nick);
}
diff --git a/utilities/cursedchat/cursedchat.c b/utilities/cursedchat/cursedchat.c
index 5da726a..fe00302 100644
--- a/utilities/cursedchat/cursedchat.c
+++ b/utilities/cursedchat/cursedchat.c
@@ -23,9 +23,11 @@
#include <curses.h>
#include <readline/readline.h>
+#define HALFSCREEN (LINES>4?(LINES-3)/2:1)
WINDOW* topic;
WINDOW* chat;
WINDOW* input;
+int chatscroll=-1;
int to_app;
// Translate ANSI escape codes to curses commands and write the text to a window
@@ -71,6 +73,11 @@ void waddansi(WINDOW* w, char* str)
}
}
+void drawchat(void)
+{
+ prefresh(chat, (chatscroll>-1?chatscroll:getcury(chat)-LINES+4), 0, 1, 0, LINES-3, COLS);
+}
+
void gotline(char* line)
{
if(!line){close(to_app); return;} // TODO: handle EOF on stdin better?
@@ -80,7 +87,7 @@ void gotline(char* line)
write(to_app, "\n", 1);
// TODO: grab user's nick for this
wprintw(chat, "\n%s: %s", "You", line);
- wrefresh(chat);
+ drawchat();
}
unsigned int bytestochars(const char* buf, unsigned int buflen, unsigned int bytes)
@@ -117,27 +124,29 @@ int escinput(int a, int byte)
if(!strcmp(buf, "[H")||!strcmp(buf, "OH")){rl_beg_of_line(1,27);return 0;}
if(!strcmp(buf, "[F")||!strcmp(buf, "OF")){rl_end_of_line(1,27);return 0;}
if(!strcmp(buf, "[3")&&read(0, buf, 1)&&buf[0]=='~'){rl_delete(1,27);return 0;}
- if(!strcmp(buf, "[5"))
+ if(!strcmp(buf, "[5")) // Page up
{
read(0, buf, 1);
- wprintw(chat, "\nTODO: handle Page up");
-// wscrl(chat, chat->_maxy/2);
- wrefresh(chat);
+ if(chatscroll<0){chatscroll=getcury(chat)-LINES+4;}
+ chatscroll-=HALFSCREEN; // (LINES-3)/2;
+ if(chatscroll<0){chatscroll=0;}
+ drawchat();
return 0;
}
- if(!strcmp(buf, "[6"))
+ if(!strcmp(buf, "[6")) // Page down
{
read(0, buf, 1);
- wprintw(chat, "\nTODO: handle Page down");
-// wscrl(chat, -chat->_maxy/2);
- wrefresh(chat);
+ if(chatscroll<0){return 0;} // Already at the bottom
+ chatscroll+=HALFSCREEN; // (LINES-3)/2;
+ if(chatscroll>getcury(chat)-LINES+3){chatscroll=-1;}
+ drawchat();
return 0;
}
// wprintw(chat, "\nbuf: %s", buf);
return 0;
}
-void drawinput()
+void drawinput(void)
{
werase(input);
unsigned int pos=bytestochars(rl_line_buffer, rl_end, rl_point);
@@ -160,15 +169,14 @@ void resizechat(int sig)
if(size.ws_row<3){return;} // Too small, would result in negative numbers breaking the chat window
resize_term(size.ws_row, size.ws_col);
wresize(topic, 1, COLS);
- wresize(chat, LINES-3, COLS);
+ wresize(chat, chat->_maxy+1, COLS);
wresize(input, 2, COLS);
mvwin(input, LINES-2, 0);
redrawwin(chat);
redrawwin(topic);
redrawwin(input);
- wrefresh(chat);
+ drawchat();
wrefresh(topic);
-// wrefresh(input);
drawinput();
}
@@ -182,19 +190,20 @@ int main(int argc, char** argv)
cbreak();
noecho();
keypad(w, 1);
+ use_default_colors();
topic=newwin(1, COLS, 0, 0);
init_pair(1, COLOR_WHITE, COLOR_BLUE);
// Define colors mapped to ANSI color codes (at least the ones tc_client uses)
- init_pair(2, COLOR_RED, 0);
- init_pair(3, COLOR_GREEN, 0);
- init_pair(4, COLOR_YELLOW, 0);
- init_pair(5, COLOR_BLUE, 0);
- init_pair(6, COLOR_MAGENTA, 0);
- init_pair(7, COLOR_CYAN, 0);
+ init_pair(2, COLOR_RED, -1);
+ init_pair(3, COLOR_GREEN, -1);
+ init_pair(4, COLOR_YELLOW, -1);
+ init_pair(5, COLOR_BLUE, -1);
+ init_pair(6, COLOR_MAGENTA, -1);
+ init_pair(7, COLOR_CYAN, -1);
wbkgd(topic, COLOR_PAIR(1)|' ');
- chat=newwin(LINES-3, COLS, 1, 0);
+ chat=newpad(2048, COLS);
scrollok(chat, 1);
input=newwin(2, COLS, LINES-2, 0);
scrollok(input, 1);
@@ -247,7 +256,7 @@ int main(int argc, char** argv)
}
waddstr(chat, "\n");
waddansi(chat, buf);
- wrefresh(chat);
+ drawchat();
wrefresh(input);
continue;
}
@@ -255,22 +264,6 @@ int main(int argc, char** argv)
p[0].revents=0;
rl_callback_read_char();
drawinput();
-#if 0
-// TODO: move this into a function and also call it when the terminal is resized
- werase(input);
- unsigned int pos=bytestochars(rl_line_buffer, rl_end, rl_point);
-
- waddstr(input, "> ");
- int cursor_row=(pos+2)/COLS;
- int end_row=(rl_end+2)/COLS;
- // Figure out how much of the buffer to print to not scroll past the cursor
-// unsigned int eol=cursor_row;// (pos+2)/COLS;
- unsigned int eol=charstobytes(rl_line_buffer, rl_end, (cursor_row+2)*COLS-3); // -2 for cursor, -1 to avoid wrapping
- waddnstr(input, rl_line_buffer, eol);
-
- wmove(input, cursor_row==end_row && cursor_row>0, (pos+2)%COLS); // +2 for prompt
- wrefresh(input);
-#endif
}
rl_callback_handler_remove();
endwin();
diff --git a/utilities/modbot/modbot.c b/utilities/modbot/modbot.c
index 5967088..1041544 100644
--- a/utilities/modbot/modbot.c
+++ b/utilities/modbot/modbot.c
@@ -403,7 +403,7 @@ int main(int argc, char** argv)
++listed;
}
}
- say(pm, "%u videos in queue, %u of which are not yet approved by mods (%s%s)\n", queue.itemcount, notapproved, buf, (listed<notapproved)?", etc.":"");
+ say(pm, "%u video%s in queue, %u of which are not yet approved by mods (%s%s)\n", queue.itemcount, (queue.itemcount==1)?"":"s", notapproved, buf, (listed<notapproved)?", etc.":"");
}else{
say(pm, "%u videos in queue\n", queue.itemcount);
}
@@ -565,6 +565,8 @@ int main(int argc, char** argv)
list_save(&goodvids, "goodvids.txt");
free(playing);
playing=strdup(vid);
+ free(requester);
+ requester=strdup(nick);
unsigned int pos=(end?(strtol(&end[1], 0, 0)/1000):0);
alarm(getduration(playing)-pos);
started=time(0)-pos;