Usermode/irc - New code working, glitches on wrapped messages (swapping windows might...
authorJohn Hodge <[email protected]>
Sun, 18 May 2014 05:56:48 +0000 (13:56 +0800)
committerJohn Hodge <[email protected]>
Sun, 18 May 2014 05:56:48 +0000 (13:56 +0800)
Usermode/Applications/irc_src/input.c
Usermode/Applications/irc_src/main.c
Usermode/Applications/irc_src/server.c
Usermode/Applications/irc_src/server.h
Usermode/Applications/irc_src/window.c
Usermode/Applications/irc_src/window.h

index 2236152..b99935c 100644 (file)
@@ -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);
index e3e2af4..6169519 100755 (executable)
@@ -7,6 +7,7 @@
 #include <string.h>
 #include <net.h>
 #include <stdarg.h>
+#include <acess/devices/pty.h>
 
 #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();
 }
index c8bfea5..1b85df4 100644 (file)
@@ -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
                // <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);
@@ -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);
index 0fd4f51..edc9bd0 100644 (file)
@@ -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)));
 
index 1314cec..db3e4da 100644 (file)
@@ -7,6 +7,8 @@
 #include <stdio.h>     // TODO: replace with calls into ACurses_*
 #include <stdlib.h>
 #include <assert.h>
+#include "server.h"
+#include <ctype.h>
 
 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);
+}
 
index da97f3e..620e1a5 100644 (file)
@@ -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);
 

UCC git Repository :: git.ucc.asn.au