From 51a617e55cb916736d30c2ac3920030048d1877c Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 18 May 2014 13:56:48 +0800 Subject: [PATCH] Usermode/irc - New code working, glitches on wrapped messages (swapping windows might fix) --- Usermode/Applications/irc_src/input.c | 15 ++- Usermode/Applications/irc_src/main.c | 29 +++-- Usermode/Applications/irc_src/server.c | 93 ++++++++++++-- Usermode/Applications/irc_src/server.h | 1 + Usermode/Applications/irc_src/window.c | 166 ++++++++++++++++++++----- Usermode/Applications/irc_src/window.h | 2 + 6 files changed, 250 insertions(+), 56 deletions(-) diff --git a/Usermode/Applications/irc_src/input.c b/Usermode/Applications/irc_src/input.c index 2236152a..b99935c0 100644 --- a/Usermode/Applications/irc_src/input.c +++ b/Usermode/Applications/irc_src/input.c @@ -42,9 +42,11 @@ void Input_HandleSelect(int nfds, const fd_set *rfds) } 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); } } } @@ -104,6 +106,15 @@ void Cmd_window(char *ArgString) } } +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); diff --git a/Usermode/Applications/irc_src/main.c b/Usermode/Applications/irc_src/main.c index e3e2af48..6169519c 100755 --- a/Usermode/Applications/irc_src/main.c +++ b/Usermode/Applications/irc_src/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "common.h" #include "input.h" @@ -35,6 +36,11 @@ void ExitHandler(void) { 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) @@ -55,24 +61,21 @@ int main(int argc, const char *argv[], const char *envp[]) 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(); @@ -83,10 +86,14 @@ int main(int argc, const char *argv[], const char *envp[]) 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; @@ -121,7 +128,7 @@ int ParseArguments(int argc, const char *argv[]) void Redraw_Screen(void) { printf("\x1B[2J"); // Clear screen - printf("\x1B[0;0H"); // Reset cursor + printf("\x1B[H"); // Reset cursor Windows_RepaintCurrent(); } diff --git a/Usermode/Applications/irc_src/server.c b/Usermode/Applications/irc_src/server.c index c8bfea58..1b85df4f 100644 --- a/Usermode/Applications/irc_src/server.c +++ b/Usermode/Applications/irc_src/server.c @@ -16,8 +16,9 @@ void ParseServerLine(tServer *Server, char *Line); // === 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 === @@ -109,7 +110,11 @@ tServer *Server_Connect(const char *Name, const char *AddressString, short PortN 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, ...) @@ -134,16 +139,40 @@ void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char { 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); } /** @@ -226,20 +255,38 @@ void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char * 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 // = :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 // :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); @@ -255,11 +302,9 @@ void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, 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'; @@ -290,6 +335,28 @@ void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, 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); diff --git a/Usermode/Applications/irc_src/server.h b/Usermode/Applications/irc_src/server.h index 0fd4f515..edc9bd0a 100644 --- a/Usermode/Applications/irc_src/server.h +++ b/Usermode/Applications/irc_src/server.h @@ -22,6 +22,7 @@ extern tServer *Server_Connect(const char *Name, const char *AddressString, shor 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))); diff --git a/Usermode/Applications/irc_src/window.c b/Usermode/Applications/irc_src/window.c index 1314cec3..db3e4da8 100644 --- a/Usermode/Applications/irc_src/window.c +++ b/Usermode/Applications/irc_src/window.c @@ -7,6 +7,8 @@ #include // TODO: replace with calls into ACurses_* #include #include +#include "server.h" +#include struct sMessage { @@ -14,6 +16,7 @@ struct sMessage time_t Timestamp; enum eMessageClass Class; char *Source; // Pointer to the end of `Data` + size_t PrefixLen; char Data[]; }; @@ -28,12 +31,16 @@ struct sWindow // === 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; @@ -41,59 +48,133 @@ 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; } @@ -203,10 +284,18 @@ void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char 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; @@ -215,8 +304,15 @@ void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char // 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); } } @@ -240,4 +336,14 @@ void Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *T { 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); +} diff --git a/Usermode/Applications/irc_src/window.h b/Usermode/Applications/irc_src/window.h index da97f3e4..620e1a5d 100644 --- a/Usermode/Applications/irc_src/window.h +++ b/Usermode/Applications/irc_src/window.h @@ -24,6 +24,8 @@ extern void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, cons 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); -- 2.20.1