#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)
+{
+ _SysDebug("Windows_int_PaintMessageLine: Message=%p,Offset=%i,EnablePrint=%b",
+ Message, (int)Offset, EnablePrint);
+ if( Offset > strlen(Message->Data) ) {
+ _SysDebug("Windows_int_PaintMessageLine: No message left");
+ return 0;
}
+ _SysDebug("Windows_int_PaintMessageLine: Message->Data=\"%.*s\"+\"%s\"",
+ (int)Offset, Message->Data, Message->Data+Offset);
+ size_t avail = giTerminal_Width - Message->PrefixLen;
+ const char *msg_data = Message->Data + Offset;
+ int used = WordBreak(msg_data, avail);
+
+ if( EnablePrint )
+ {
+ if( Offset == 0 )
+ Windows_int_PaintMessagePrefix(Message, true);
+ else {
+ for(int i = 0; i < Message->PrefixLen; i ++)
+ printf(" ");
+ //printf("\x1b[%iC", Message->PrefixLen);
+ }
+ printf("%.*s", used, msg_data);
+ }
+
+ _SysDebug("used(%i) >= strlen(msg_data)(%i)", used, strlen(msg_data));
+ if( used >= strlen(msg_data) )
+ 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[S"); // Scroll down 1 (free space below)
+ SetCursorPos(giTerminal_Height-2, 1);
+ len = Windows_int_PaintMessageLine(msg, 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);
+}