}
free(cmd);
// Prompt
- SetCursorPos(giTerminal_Height-1, 1);
+ SetCursorPos(giTerminal_Height, 1);
printf("\x1B[2K"); // Clear line
- printf("[%s]", Window_GetName(NULL));
+ int prompt_len = printf("[%s] ", Window_GetName(NULL));
+ SetCursorPos(giTerminal_Height, prompt_len+1);
+ fflush(stdout);
}
}
}
}
}
+void Cmd_me(char *ArgString)
+{
+ tServer *srv = Window_GetServer(NULL);
+ if( srv && Window_IsChat(NULL) ) {
+ Window_AppendMessage(NULL, MSG_CLASS_ACTION, Server_GetNick(srv), "%s", ArgString);
+ Server_SendCommand(srv, "PRIVMSG %s :\1ACTION %s\1\n", Window_GetName(NULL), ArgString);
+ }
+}
+
const struct {
const char *Name;
void (*Fcn)(char *ArgString);
#include <string.h>
#include <net.h>
#include <stdarg.h>
+#include <acess/devices/pty.h>
#include "common.h"
#include "input.h"
{
printf("\x1B[?1047l");
printf("Quit: %s\n", gsExitReason);
+
+ // stty +echo,canon
+ struct ptymode mode = {.InputMode = 0, .OutputMode = 0};
+ mode.InputMode = PTYIMODE_CANON|PTYIMODE_ECHO;
+ _SysIOCtl(0, PTY_IOCTL_SETMODE, &mode);
}
void Exit(const char *Reason)
ACurses_Init();
printf("\x1B[?1047h");
- printf("\x1B[%i;%ir", 1, giTerminal_Height-1);
-
- SetCursorPos(giTerminal_Height-1, 1);
- printf("[(status)] ");
+ printf("\x1B[%i;%ir", 2, giTerminal_Height-2);
// HACK: Static server entry
// UCC (University [of Western Australia] Computer Club) IRC Server
- tServer *starting_server = Server_Connect( "UCC", "130.95.13.18", 6667 );
+// tServer *starting_server = Server_Connect( "UCC", "130.95.13.18", 6667 );
// Freenode (#osdev)
-// gWindow_Status.Server = Server_Connect( "Freenode", "89.16.176.16", 6667 );
+ tServer *starting_server = Server_Connect( "Freenode", "84.240.3.129", 6667 );
// Local servers
// gWindow_Status.Server = Server_Connect( "VMHost", "10.0.2.2", 6667 );
// gWindow_Status.Server = Server_Connect( "BitlBee", "192.168.1.39", 6667 );
- if( !starting_server )
- return -1;
-
Windows_SetStatusServer(starting_server);
+ Windows_RepaintCurrent();
+ SetCursorPos(giTerminal_Height-1, 1);
+ printf("[(status)] ");
MainLoop();
int MainLoop(void)
{
- SetCursorPos(giTerminal_Height-1, 1);
+ SetCursorPos(giTerminal_Height, 1);
printf("[(status)] ");
fflush(stdout);
+ // stty -echo,canon
+ struct ptymode mode = {.InputMode = 0, .OutputMode = 0};
+ _SysIOCtl(0, PTY_IOCTL_SETMODE, &mode);
+
for( ;; )
{
fd_set readfds, errorfds;
void Redraw_Screen(void)
{
printf("\x1B[2J"); // Clear screen
- printf("\x1B[0;0H"); // Reset cursor
+ printf("\x1B[H"); // Reset cursor
Windows_RepaintCurrent();
}
// === GLOBALS ===
const char *gsUsername = "user";
const char *gsHostname = "acess";
-const char *gsRealName = "Acess2 IRC Client";
+const char *gsRealName = "An Acess User";
const char *gsNickname = "acess";
+const char *gsVersionResponse = "Acess2 IRC Client / Running on some VM probably";
tServer *gpServers;
// === CODE ===
const char *Server_GetNick(const tServer *Server)
{
- return Server->Nick;
+ return Server ? Server->Nick : "NULL";
+}
+const char *Server_GetName(const tServer *Server)
+{
+ return Server ? Server->Name : "NULL";
}
void Server_SendCommand(tServer *Server, const char *Format, ...)
{
tWindow *win;
if( strcmp(Dest, Server->Nick) == 0 ) {
- win = Windows_GetByName(Server, Src);
- if(!win)
- win = Window_Create(Server, Src);
+ win = Windows_GetByNameOrCreate(Server, Src);
}
else {
- win = Windows_GetByName(Server, Dest);
- if(!win)
- win = Window_Create(Server, Dest);
+ win = Windows_GetByNameOrCreate(Server, Dest);
+ }
+
+ // Detect CTCP
+ if( Message[0] == '\1' && Message[strlen(Message)-1] == '\1' )
+ {
+ Message += 1;
+ // message is a CTCP command
+ if( strcmp(Message, "VERSION\1") == 0 )
+ {
+ // Put a message in the status window, and reply
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "CTCP VERSION request from %s", Src);
+ // - Always reply via NOTICE
+ Server_SendCommand(Server, "NOTICE %s :\1VERSION %s\1", Src, gsVersionResponse);
+ }
+ else if( strncmp(Message, "ACTION ", 7) == 0 )
+ {
+ Message += 7;
+ // Put a message in the status window, and reply
+ Window_AppendMessage(win, MSG_CLASS_ACTION, Src, "%.*s", (int)(strlen(Message)-1), Message);
+ }
+ else
+ {
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "Unknown CTCP '%s' from %s",
+ Message, Src);
+ }
+ }
+ else
+ {
+ Window_AppendMessage(win, MSG_CLASS_MESSAGE, Src, "%s", Message);
}
- Window_AppendMessage(win, MSG_CLASS_MESSAGE, Src, "%s", Message);
}
/**
Window_AppendMsg_TopicTime( Windows_GetByNameOrCreate(Server, user), message, timestamp );
break;
case 353: // /NAMES list
+ // TODO: Parse the /names list and store it locally, dump to window when end is seen
// <user> = <channel> :list
// '=' was eaten in and set to message
user = GetValue(Line, &pos); // Actually channel
message = Line + pos + 1; // List
- // TODO: parse and store
Window_AppendMessage( Windows_GetByNameOrCreate(Server, user), MSG_CLASS_CLIENT, "NAMES", message );
break;
case 366: // end of /NAMES list
// <user> <channel> :msg
// - Ignored
break;
+
+ case 1: // welcome
+ case 2: // host name and version (text)
+ case 3: // host uptime
+ case 4: // host name, version, and signature
+ case 5: // parameters
+
+ case 250: // Highest connection count
+ case 251: // user count (network)
+ case 252: // Operator count
+ case 253: // Unidentified connections
+ case 254: // Channel count
+ case 255: // Server's stats
+ case 265: // Local users -- min max :Text representation
+ case 266: // Global users (same as above)
+
case 372: // MOTD Data
case 375: // MOTD Start
case 376: // MOTD End
+ Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "%s", message);
+ break;
default:
//printf("[%s] %i %s\n", Server->Name, num, message);
if( strcmp(cmd, "NOTICE") == 0 )
{
const char *class = GetValue(Line, &pos);
- _SysDebug("NOTICE class='%s'", class);
-
const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
+ _SysDebug("NOTICE class='%s'", class);
- //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
char *ident_bang = strchr(ident, '!');
if( ident_bang ) {
*ident_bang = '\0';
Window_AppendMsg_Part( Windows_GetByNameOrCreate(Server, channel), ident, "" );
}
+ else if( strcmp(cmd, "MODE" ) == 0 )
+ {
+ // ident MODE channel flags nick[ nick...]
+ const char *channel = GetValue(Line, &pos);
+ const char *flags = GetValue(Line, &pos);
+ const char *args = Line + pos;
+
+ Window_AppendMsg_Mode( Windows_GetByNameOrCreate(Server, channel), ident, flags, args );
+ }
+ else if( strcmp(cmd, "KICK" ) == 0 )
+ {
+ // ident KICK channel nick :reason
+ const char *channel = GetValue(Line, &pos);
+ const char *nick = GetValue(Line, &pos);
+ const char *message = Line + pos + 1;
+
+ Window_AppendMsg_Kick( Windows_GetByNameOrCreate(Server, channel), ident, nick, message );
+ if( strcmp(nick, Server->Nick) == 0 ) {
+ // Oh, that was me :(
+ // - what do?
+ }
+ }
else
{
Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_BARE, Server->Name, "Unknown command '%s' %s", cmd, Line);
extern int Server_HandleIncoming(tServer *Server);
extern const char *Server_GetNick(const tServer *Server);
+extern const char *Server_GetName(const tServer *Server);
extern void Server_SendCommand(tServer *Server, const char *Format, ...) __attribute__((format(__printf__,2,3)));
#include <stdio.h> // TODO: replace with calls into ACurses_*
#include <stdlib.h>
#include <assert.h>
+#include "server.h"
+#include <ctype.h>
struct sMessage
{
time_t Timestamp;
enum eMessageClass Class;
char *Source; // Pointer to the end of `Data`
+ size_t PrefixLen;
char Data[];
};
// === PROTOTYPES ===
void Windows_RepaintCurrent(void);
+size_t WordBreak(const char *Line, size_t avail);
+size_t Windows_int_PaintMessagePrefix(const tMessage *Message, bool EnablePrint);
+size_t Windows_int_PaintMessageLine(const tMessage *Message, size_t Offset, bool EnablePrint);
+ int Windows_int_GetMessageLines(const tMessage *Message);
int Windows_int_PaintMessage(tMessage *Message);
// === GLOBALS ===
tWindow gWindow_Status = {
NULL, NULL, NULL, // No next, empty list, no server
- 0, {""} // No activity, empty name (rendered as status)
+ 0, {"(status)"} // No activity, empty name (rendered as status)
};
tWindow *gpWindows = &gWindow_Status;
tWindow *gpCurrentWindow = &gWindow_Status;
// === CODE ===
void Windows_RepaintCurrent(void)
{
- tMessage *msg = gpCurrentWindow->Messages;
-
// TODO: Title bar?
+ SetCursorPos(1, 1);
+ printf("\x1b[37;44m\x1b[2K%s\x1b[0m\n", gpCurrentWindow->Name);
+
+ int avail_rows = giTerminal_Height - 3;
// Note: This renders from the bottom up
- for( int y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
+ tMessage *msg = gpCurrentWindow->Messages;
+ for( int y = avail_rows; msg && y > 0; )
{
- y -= Windows_int_PaintMessage(msg);
+ int lines = Windows_int_GetMessageLines(msg);
+ y -= lines;
+ size_t ofs = 0;
+ size_t len;
+ int i = 0;
+ do {
+ SetCursorPos(2 + y+i, 1);
+ len = Windows_int_PaintMessageLine(msg, ofs, (y+i >= 0));
+ ofs += len;
+ i ++;
+ } while( len > 0 );
+ msg = msg->Next;
}
+ // Status line is our department
+ SetCursorPos(giTerminal_Height-1, 1);
+ printf("\x1b[37;44m\x1b[2K[%s] [%s/%s]\x1b[0m", Server_GetNick(gpCurrentWindow->Server),
+ Server_GetName(gpCurrentWindow->Server), gpCurrentWindow->Name);
+ fflush(stdout);
// Bottom line is rendered by the prompt
+}
+
+size_t WordBreak(const char *Line, size_t avail)
+{
+ // If sufficient space, don't need to break on a word
+ if( strlen(Line) < avail )
+ return strlen(Line);
+
+ // Search backwards from end of space for a non-alpha-numeric character
+ size_t ret = avail-1;
+ while( ret > 0 && isalnum(Line[ret]) )
+ ret --;
+
+ // if one wasn't found in a sane area, just split
+ if( ret < avail-20 || ret == 0 )
+ return avail;
+ return ret;
}
-int Windows_int_PaintMessage(tMessage *Message)
+size_t Windows_int_PaintMessagePrefix(const tMessage *Message, bool EnablePrint)
{
- printf("\33[T"); // Scroll down 1 (free space below)
- SetCursorPos(giTerminal_Height-2, 1);
+ size_t len = 0;
+
+ unsigned long seconds_today = Message->Timestamp/1000 % (24 * 3600);
+ if(EnablePrint)
+ printf("%02i:%02i:%02i ", seconds_today/3600, (seconds_today/60)%60, seconds_today%60);
+ else
+ len += snprintf(NULL, 0, "%02i:%02i:%02i ", seconds_today/3600, (seconds_today/60)%60, seconds_today%60);
- size_t prefixlen = 0;
- prefixlen += printf("%02i:%02i:%02i ", (Message->Timestamp/3600)%24, (Message->Timestamp/60)%60, Message->Timestamp%60);
+ const char *format;
switch(Message->Class)
{
- case MSG_CLASS_BARE: break;
+ case MSG_CLASS_BARE:
+ format = "";
+ break;
case MSG_CLASS_CLIENT:
if(Message->Source)
- prefixlen += printf("[%s] ", Message->Source);
- prefixlen += printf("-!- ");
+ format = "[%s] -!- ";
+ else
+ format = "-!- ";
break;
case MSG_CLASS_WALL:
- prefixlen += printf("[%s] ", Message->Source);
+ format = "[%s] ";
break;
case MSG_CLASS_MESSAGE:
- prefixlen += printf("<%s> ", Message->Source);
+ format = "<%s> ";
break;
case MSG_CLASS_ACTION:
- prefixlen += printf("* %s ", Message->Source);
+ format = "* %s ";
break;
}
- int avail = giTerminal_Width - prefixlen;
- int msglen = strlen(Message->Data);
-
- int nLines = 1;
- printf("%.*s", avail, Message);
- while( msglen > avail ) {
- msglen -= avail;
- Message += avail;
- printf("\33[T");
- SetCursorPos(giTerminal_Height-2, prefixlen+1);
- printf("%.*s", avail, Message);
- nLines ++;
+
+ if( EnablePrint )
+ len += printf(format, Message->Source);
+ else
+ len += snprintf(NULL, 0, format, Message->Source);
+
+ return len;
+}
+
+size_t Windows_int_PaintMessageLine(const tMessage *Message, size_t Offset, bool EnablePrint)
+{
+ if( Message->Data[Offset] == '\0' ) {
+ return 0;
}
+ size_t avail = giTerminal_Width - Message->PrefixLen;
+ const char *msg_data = Message->Data + Offset;
+ int used = WordBreak(msg_data+Offset, avail);
+
+ if( EnablePrint )
+ {
+ if( Offset == 0 )
+ Windows_int_PaintMessagePrefix(Message, true);
+ else
+ printf("\x1b[%iC", Message->PrefixLen);
+ printf("%.*s", used, msg_data);
+ }
+
+ if( msg_data[used] == '\0' )
+ return 0;
+
+ return Offset + used;
+}
+
+int Windows_int_GetMessageLines(const tMessage *Message)
+{
+ assert(Message->PrefixLen);
+ const size_t avail = giTerminal_Height - Message->PrefixLen;
+ const size_t msglen = strlen(Message->Data);
+ size_t offset = 0;
+ int nLines = 0;
+ do {
+ offset += WordBreak(Message->Data+offset, avail);
+ nLines ++;
+ } while(offset < msglen);
return nLines;
}
msg->Class = Class;
msg->Source = (Source ? msg->Data + len+1 : NULL);
+ msg->Timestamp = _SysTimestamp();
+
va_start(args, Message);
vsnprintf(msg->Data, len+1, Message, args);
va_end(args);
+ if( Source ) {
+ strcpy(msg->Source, Source);
+ }
+
+ msg->PrefixLen = Windows_int_PaintMessagePrefix(msg, false);
+
msg->Next = Window->Messages;
Window->Messages = msg;
// Scroll if needed, and redraw?
// - Lazy option of draw at bottom of screen
printf("\33[s"); // Save cursor
- Windows_int_PaintMessage(msg);
- printf("\x1b[u"); // Restore cursor
+ size_t offset = 0, len;
+ do {
+ printf("\33[T"); // Scroll down 1 (free space below)
+ SetCursorPos(giTerminal_Height-2, 1);
+ len = Windows_int_PaintMessageLine(Message, offset, true);
+ offset += len;
+ } while( len > 0 );
+ printf("\33[u"); // Restore cursor
+ fflush(stdout);
}
}
{
Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic set by %s at %s", User, Timestamp);
}
+void Window_AppendMsg_Kick(tWindow *Window, const char *Operator, const char *Nick, const char *Reason)
+{
+ Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s was kicked from %s by %s [%s]",
+ Nick, Window->Name, Operator, Reason);
+}
+void Window_AppendMsg_Mode(tWindow *Window, const char *Operator, const char *Flags, const char *Args)
+{
+ Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "mode/%s [%s %s] by %s",
+ Window->Name, Flags, Args, Operator);
+}
extern void Window_AppendMsg_Join(tWindow *Window, const char *Usermask);
extern void Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason);
extern void Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason);
+extern void Window_AppendMsg_Kick(tWindow *Window, const char *Operator, const char *Nick, const char *Reason);
+extern void Window_AppendMsg_Mode(tWindow *Window, const char *Operator, const char *Flags, const char *Args);
extern void Window_AppendMsg_Topic(tWindow *Window, const char *Topic);
extern void Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestmap);