1 //-----------------------------------------------------------------------------
2 // Console
3 //-----------------------------------------------------------------------------
4 
5 #include "cake.h"
6 #include "console.h"
7 #include "definitions.h"
8 #include "alias.h"
9 #include "commands.h"
10 #include "logfile.h"
11 #include "overlay.h"
12 #include "render.h"
13 #include "timer.h"
14 #include "vars.h"
15 #include "mem.h"
16 #include "system.h"
17 
18 #ifdef WIN32
19   #include <tchar.h>        // pour _vsntprintf
20 #else
21   #include <stdlib.h>
22   #include <stdio.h>
23   #include <stdarg.h>
24   //#include <sys/va_list.h>
25   #define _vsntprintf vsnprintf
26 #endif
27 
28 float opening_or_closing_progress = 1.f;
29 int currently_used_message_lines = 0;
30 int prompt_len = 0, cons_i, cons_j;
31 char final_buffer[N_LINES_MAX][CONSOLE_LINELENGTH] = { { '\0'} };
32 
33 Var v_enablelog("v_enablelog", 1);
34 
35 //-----------------------------------------------------------------------------------------
36 
Init(void)37 void ConsLine::Init(void)
38 {
39   memset(content, '\0', CONSOLE_LINELENGTH*sizeof(char));
40   length = 0;
41   nlines = 1;
42   breakPos = (int*) cake_malloc(sizeof(int), "ConsLine::Init.breakPos");
43   if (!breakPos) ThrowException(ALLOCATION_ERROR, "ConsLine::Init.breakPos");
44   breakPosSize = 1;
45   breakPos[0] = 0;
46 }
47 
Shut(void)48 void ConsLine::Shut(void)
49 {
50   cake_free(breakPos);
51   breakPos = NULL;
52 }
53 
ReInit(void)54 void ConsLine::ReInit(void)
55 {
56   memset(content, '\0', CONSOLE_LINELENGTH*sizeof(char));
57   length = 0;
58   nlines = 1;
59   breakPosSize = 1;
60   breakPos[0] = 0;
61 }
62 
Update(int linelen)63 int ConsLine::Update(int linelen)
64 {
65   nlines = 1;
66 
67   int llen = 0, i = 0;
68   while (i < length)
69   {
70     if (content[i] == TEXT_STYLE_KEYWORD)
71     {
72       ++i;
73       if (content[i] == 'a' || content[i] == 'c')
74         while (i < length && content[i] != '}') ++i;
75       else if (content[i] == 'i' || content[i] == 'b') ++i;
76       continue;
77     }
78     else if (content[i] == '^')
79     {
80       if (i < length-1) i += 2;
81       continue;
82     }
83 
84     if (llen >= linelen)  // line break
85     {
86       ++nlines;
87       if (nlines > breakPosSize)
88       {
89         breakPos = (int*) cake_realloc(breakPos, nlines*sizeof(int), "ConsLine::Update.breakPos");
90         breakPosSize = nlines;
91       }
92 
93       // try to find a better place for cutting
94       int z = i-1;
95 
96       if (nlines > 1)
97       {
98         while (z > breakPos[nlines-2] && content[z] != ' ') --z;
99         if (z <= breakPos[nlines-2]) z = i;
100         else ++z;
101       }
102       else
103       {
104         while (z > 0 && content[z] != ' ') --z;
105         if (z <= 0) z = i;
106         else ++z;
107       }
108       i = z;
109       breakPos[nlines-1] = i;
110       llen = 1;
111     }
112     else
113     {
114       ++llen;
115     }
116     ++i;
117   }
118 
119 #if 0
120   printf("------------------------------------\n");
121   for (int a = 0; a < nlines; ++a) printf("%d : %d\n", a, breakPos[a]);
122 #endif
123 
124   return nlines;
125 }
126 
SetContent(const char * c)127 void ConsLine::SetContent(const char *c)
128 {
129   memset(content, '\0', CONSOLE_LINELENGTH*sizeof(char));
130   strcpy(content, c);
131   length = (int) strlen(content);
132 }
133 
AddContent(const char * c)134 int ConsLine::AddContent(const char *c)
135 {
136   if ((int) strlen(content) + (int) strlen(c) < CONSOLE_LINELENGTH) strcat(content, c);
137   else return 0;
138   length = (int) strlen(content);
139   return length;
140 }
141 
GetContent(int p)142 char* ConsLine::GetContent(int p)
143 {
144   memset(buffer, '\0', CONSOLE_LINELENGTH*sizeof(char));
145   if (p < 0 || p >= nlines) return content;
146   else
147   {
148     if (p == nlines - 1) memcpy(buffer, &content[breakPos[p]], (length-breakPos[p])*sizeof(char));
149     else memcpy(buffer, &content[breakPos[p]], (breakPos[p+1]-breakPos[p])*sizeof(char));
150   }
151 
152   // Recopie les derniers param�tres de couleur en d�but de string
153   if (p > 0)
154   {
155     char tmp[CONSOLE_LINELENGTH];
156     memset(tmp, 0, CONSOLE_LINELENGTH*sizeof(char));
157 
158     for (int k = 0, t = 0; k < breakPos[p];)
159     {
160       if (content[k] == TEXT_STYLE_KEYWORD)
161       {
162         tmp[t++] = content[k++];  // TEXT_STYLE_KEYWORD
163         if (k >= length) return buffer;
164         if (content[k] == 'c' || content[k] == 'a')
165         {
166           while (k < length && content[k] != '}') tmp[t++] = content[k++];
167           if (k < length) tmp[t++] = content[k++];
168         }
169         else if (content[k] == 'i' || content[k] == 'b')
170         {
171           tmp[t++] = content[k++];  // i || b
172           tmp[t++] = content[k++];  // + || -
173         }
174       }
175       else if (content[k] == '^')
176       {
177         tmp[t++] = content[k++];  // '^'
178         if (k >= length) return buffer;
179         tmp[t++] = content[k++];  // valeur
180       }
181       else ++k;
182     }
183 
184     strcat(tmp, buffer);
185     strcpy(buffer, tmp);
186 
187 #if 0
188     printf("%s\n", buffer);
189 #endif
190   }
191 
192   return buffer;
193 }
194 
195 //-----------------------------------------------------------------------------------------
196 
cmd_close(int argc,char * argv[])197 void cmd_close(int argc, char *argv[])
198 {
199   gConsole->Close();
200 }
201 
cmd_messages(int argc,char * argv[])202 void cmd_messages(int argc, char *argv[])
203 {
204   if (argc > 1)
205   {
206     if (!stricmp(argv[1],"on") || !stricmp(argv[1],"1")) gConsole->showMessages = true;
207     else if (!stricmp(argv[1],"off") || !stricmp(argv[1],"0")) gConsole->showMessages = false;
208     else gConsole->Insertln("^5Argument not valid");
209   }
210   else gConsole->Insertln("usage: %s <value>", argv[0]);
211 }
212 
cmd_messagetimelife(int argc,char * argv[])213 void cmd_messagetimelife(int argc, char *argv[])
214 {
215   if (argc > 1) gConsole->messageMaxLife = (float) atof(argv[1]);
216   else gConsole->Insertln("usage: %s <value>", argv[0]);
217 }
218 
cmd_clear(int argc,char * argv[])219 void cmd_clear(int argc, char *argv[])
220 {
221   gConsole->Clear();
222 }
223 
Console(void)224 Console::Console(void)
225 {
226   font = -1;
227   back = -1;
228   close = -1;
229   reduce = -1;
230   maximise = -1;
231   titlebar = -1;
232   scrollUp = -1;
233   scrollDown = -1;
234   scroll = -1;
235   resize = -1;
236 
237   cursorSpeed = 2;
238   prompt = NULL;
239   cursorSymbol = '_';
240   SetPrompt("]");
241 
242   movingConsole = false;
243   topPos = 0;
244   leftPos = 0;
245   width = 640-2*leftPos;
246   height = 240;
247 
248   state = CLOSED;
249 
250   Coeff = 1;
251   SetFontSize(8, 16, 0);
252   fontRows = 16;
253   fontCols = 16;
254 
255   showMessages = true;
256   addToMessages = true;
257   messageMaxLife = 2.5f;
258   startNewMessageLine = true;
259 
260   autoCut = true;
261 
262   openSpeed = .25f;
263   closeSpeed = .25f;
264   enableOpeningClosingAnimations = true;
265 
266   cursorPos = 0;
267   scrollVal = 0;
268 
269   ActiveBorderColor[0]  = 1.f;
270   ActiveBorderColor[1]  = 1.f;
271   ActiveBorderColor[2]  = 0.f;
272   ActiveBorderColor[3]  = 1.f;
273 
274   InactiveBorderColor[0]  = 0.4f;
275   InactiveBorderColor[1]  = 0.4f;
276   InactiveBorderColor[2]  = 0.4f;
277   InactiveBorderColor[3]  = 1.f;
278 
279   showVScroll = true;
280   showTitleBar = true;
281   titleBarHeight = 18;
282 
283   int i;
284 
285   startNewLine      = true;
286   NbrAllocatedLines   = LINESBLOC;
287   NbrUsedLines      = 1;
288   NbrTrueLines      = 1;
289   ConsoleLines      = (ConsLine*) cake_malloc(NbrAllocatedLines*sizeof(ConsLine), "Console::Console.ConsoleLines");
290   for (i = 0; i < NbrAllocatedLines; ++i) ConsoleLines[i].Init();
291   memset(ConsoleLines, '\0', CONSOLE_LINELENGTH);
292 
293   memset(MessageLines, '\0', sizeof(MessageLines));
294   for (i = 0; i < C_NMESSAGELINES; ++i) MessageLife[i] = 0.f;
295 
296   if (movingConsole)
297   {
298     Resize(gRender->GetWidth() - 40, gRender->GetHeight()/2);
299     SetTopPos(20+titleBarHeight);
300     SetLeftPos(20);
301   }
302   else
303   {
304     Resize(gRender->GetWidth(), gRender->GetHeight()/2);
305     SetTopPos(0);
306     SetLeftPos(0);
307   }
308 
309   Recalculate_NLines();
310 
311   gCommands->AddCommand("close", cmd_close, "closes the console");
312   gAlias->SetAlias("hide", "close");
313   gCommands->AddCommand("showmessages", cmd_messages, "activates or disactivates the display of console messageswhenconsole is closed");
314   gCommands->AddCommand("msglife", cmd_messagetimelife, "sets the life time for console messages when console is closed");
315   gCommands->AddCommand("clear", cmd_clear, "clears the console content");
316   gAlias->SetAlias("clr", "clear");
317 
318   gVars->RegisterVar(v_enablelog);
319 }
320 
~Console(void)321 Console::~Console(void)
322 {
323   for (int i = 0; i < NbrAllocatedLines; ++i) ConsoleLines[i].Shut();
324   cake_free(ConsoleLines); ConsoleLines = NULL;
325 
326   if (prompt) cake_free(prompt);
327   prompt = NULL;
328 
329   gVars->UnregisterVar(v_enablelog);
330 }
331 
Init(void)332 void Console::Init(void)
333 {
334   // load shader that have been referenced, and load also his textures
335   // (shaders must be loaded before any other object)
336   gConsole->Insertln("Updating console shaders...");
337   shaders.Update();
338 }
339 
Shut(void)340 void Console::Shut(void)
341 {
342   gConsole->Insertln("Shuting console...");
343   shaders.ResetAll();
344   shaders.Update();
345 }
346 
Clear(void)347 void Console::Clear(void)
348 {
349   int i;
350   for (i = 0; i < NbrAllocatedLines; ++i) ConsoleLines[i].Shut();
351   cake_free(ConsoleLines);
352   startNewLine      = true;
353   NbrUsedLines      = 1;
354   cursorPos       = 0;
355   scrollVal       = 0;
356   NbrAllocatedLines   = LINESBLOC;
357   ConsoleLines = (ConsLine*) cake_malloc(NbrAllocatedLines*sizeof(ConsLine), "Console::Clear.ConsoleLines");
358   for (i = 0; i < NbrAllocatedLines; ++i) ConsoleLines[i].Init();
359 }
360 
Insert(const char * s,...)361 void Console::Insert(const char* s, ...)
362 {
363   if (!s || !strlen(s)) return;
364 
365   va_list msg;
366   char buffer[CONSOLE_LINELENGTH] = { '\0' };
367 
368   va_start(msg, s);
369   _vsntprintf(buffer, CONSOLE_LINELENGTH - 1, s, msg);
370   va_end(msg);
371 
372   if (strlen(buffer) <= 0) return;
373 
374   if (v_enablelog.ivalue) gLogFile->Insert(buffer);
375 
376   char backupCommand[CONSOLE_LINELENGTH] = { '\0' };  // backup current command line
377   if (ConsoleLines[NbrUsedLines-1].length)
378     strncpy(backupCommand, ConsoleLines[NbrUsedLines-1].content, CONSOLE_LINELENGTH);
379 
380   scrollVal = 0;
381   char final[CONSOLE_LINELENGTH] = { '\0' };
382   int l = (int) strlen(buffer);
383 
384   // Parse the input string and
385   //  -> replace tabulations with spaces
386   //  -> remove HTML syntax
387   //  -> recopy other characters
388   for (int i = 0, j = 0; i < l; ++i)
389   {
390     if (buffer[i] == '\n')
391     {
392       if (startNewLine)
393       {
394         AddLine();
395         ConsoleLines[NbrUsedLines-2].Init();
396       }
397 
398       startNewLine = true;
399 
400       if (!strlen(final)) continue;
401 
402       if (addToMessages) AddMessageLine(final);
403 
404       if (!ConsoleLines[NbrUsedLines-2].AddContent(final))
405       {
406         AddLine();
407         ConsoleLines[NbrUsedLines-2].Init();
408         if (strlen(backupCommand)) ConsoleLines[NbrUsedLines-1].SetContent(backupCommand);
409         ConsoleLines[NbrUsedLines-2].SetContent(final);
410       }
411 
412       j = 0;
413       memset(final, '\0', CONSOLE_LINELENGTH);
414     }
415     else if (buffer[i] == '\t')
416     {
417       for (int k = 0; k < TAB_LEN && j < CONSOLE_LINELENGTH-1; ++k)
418         final[j++] = ' ';
419     }
420     else if (buffer[i] == '<')
421     {
422       // TODO: don't allow \t and \n here
423       // analyse string to find if it is a valid tag
424       int backup_pos = i;
425       while (i < l && buffer[i] != '>') ++i;
426       if (i < l)
427       {
428         if (buffer[i-1] != '/' && buffer[backup_pos+1] != '/')
429         {
430           // search for closing tag
431           // get tag
432           char tag[CONSOLE_LINELENGTH] = { '\0' };
433           strncpy(tag, &buffer[backup_pos+1], i-backup_pos-1);
434           int taglen = (int) strlen(tag);
435           while (++i < l)
436           {
437             if (i < l-taglen-2 && buffer[i] == '<' && buffer[i+1] == '/' &&
438               !strnicmp(&buffer[i+2], tag, taglen) && buffer[i+2+taglen] == '>')
439             {
440               // closing tag found
441               i = backup_pos+taglen+1;  // skip '/', '>' and tag length
442               break;
443             }
444           }
445           if (i == l)
446           {
447             // no closing tag found, recopy the full string
448             i = backup_pos;
449             if (j < CONSOLE_LINELENGTH-1) final[j++] = buffer[i];
450           }
451         }
452       }
453       else
454       {
455         // no closing tag (it is a single "smaller" (<) character)
456         i = backup_pos;
457         if (j < CONSOLE_LINELENGTH-1) final[j++] = buffer[i];
458       }
459     }
460     else if (j < CONSOLE_LINELENGTH-1) final[j++] = buffer[i];  // default char, recopy it
461   }
462 
463   if (l = (int) strlen(final))
464   {
465     // clamp string into valid size
466     if (l >= CONSOLE_LINELENGTH)
467       final[CONSOLE_LINELENGTH-1] = '\0';
468 
469     if (addToMessages)
470       AddToLastMessageLine(final);
471 
472     if (startNewLine)
473     {
474       AddLine();
475       ConsoleLines[NbrUsedLines-2].Init();
476     }
477 
478     if (!ConsoleLines[NbrUsedLines-2].AddContent(final))
479     {
480       AddLine();
481       ConsoleLines[NbrUsedLines-2].Init();
482       ConsoleLines[NbrUsedLines-2].SetContent(final);
483     }
484 
485     startNewLine = false;
486   }
487 
488   if (strlen(backupCommand)) ConsoleLines[NbrUsedLines-1].SetContent(backupCommand);
489   Recalculate_NLines();
490 
491   //  if (gRender->ready && State::curr_shader && gTimer && Timer::frametime < 0.04)    // Ne le fait pas si les fps sont trop basses
492   //  {
493   //    Update();
494   //    Render();
495   //  }
496 }
497 
Insertln(const char * s,...)498 void Console::Insertln(const char* s, ...)
499 {
500   if (!s || !strlen(s)) return;
501 
502   va_list msg;
503   char buffer[CONSOLE_LINELENGTH] = { '\0' };
504 
505   va_start(msg, s);
506   _vsntprintf(buffer, CONSOLE_LINELENGTH - 1, s, msg);
507   va_end(msg);
508 
509   if (strlen(buffer) <= 0) return;
510 
511   Insert("%s\n", buffer);
512 }
513 
514 // Command line management
GetNbrUsedLines(void)515 int Console::GetNbrUsedLines(void)
516 {
517   return NbrUsedLines;
518 }
519 
520 // Ajoute un caract�re � la commande
AddChar(char c)521 void Console::AddChar(char c)
522 {
523   if ((int) ConsoleLines[NbrUsedLines-1].length > CONSOLE_LINELENGTH)
524   {
525     beep();
526     return;
527   }
528 
529   if (c == TEXT_STYLE_KEYWORD) return;
530 
531   if (c != DELETE_CHAR)
532   {
533     if (cursorPos > 0)
534     {
535       char begin[CONSOLE_LINELENGTH] = { '\0' };
536       char end[CONSOLE_LINELENGTH] = { '\0' };
537 
538       strncpy(begin, ConsoleLines[NbrUsedLines-1].content, ConsoleLines[NbrUsedLines-1].length-cursorPos);
539       strncpy(end, &ConsoleLines[NbrUsedLines-1].content[ConsoleLines[NbrUsedLines-1].length-cursorPos], CONSOLE_LINELENGTH);
540 
541       strcat(begin, " ");
542       begin[strlen(begin)-1] = c;
543       strncat(begin, end, CONSOLE_LINELENGTH);
544       ConsoleLines[NbrUsedLines-1].SetContent(begin);
545     }
546     else
547     {
548       strcat(ConsoleLines[NbrUsedLines-1].content, " ");
549       ConsoleLines[NbrUsedLines-1].content[ConsoleLines[NbrUsedLines-1].length++] = c;
550     }
551   }
552   else  // Supprime un caract�re de la commande par Delete
553   {
554     if (cursorPos)
555     {
556       char begin[CONSOLE_LINELENGTH] = { '\0' };
557       char end[CONSOLE_LINELENGTH] = { '\0' };
558 
559       strncpy(begin, ConsoleLines[NbrUsedLines-1].content, ConsoleLines[NbrUsedLines-1].length-cursorPos);
560       strncpy(end, &ConsoleLines[NbrUsedLines-1].content[ConsoleLines[NbrUsedLines-1].length-cursorPos+1], CONSOLE_LINELENGTH);
561 
562       strncat(begin, end, CONSOLE_LINELENGTH);
563       ConsoleLines[NbrUsedLines-1].SetContent(begin);
564 
565       --cursorPos;
566     }
567     else beep();
568   }
569 }
570 
571 // Supprime un caract�re de la commande par retour chariot
DelChar(void)572 void Console::DelChar(void)
573 {
574   if (ConsoleLines[NbrUsedLines-1].length == 0 || ConsoleLines[NbrUsedLines-1].length == cursorPos)
575   {
576     beep();
577     return;
578   }
579 
580   if (cursorPos > 0)
581   {
582     if (cursorPos < (int) ConsoleLines[NbrUsedLines-1].length)
583     {
584       char begin[CONSOLE_LINELENGTH] = { '\0' };
585       char end[CONSOLE_LINELENGTH] = { '\0' };
586 
587       strncpy(begin, ConsoleLines[NbrUsedLines-1].content, ConsoleLines[NbrUsedLines-1].length-cursorPos-1);
588       strncpy(end, &ConsoleLines[NbrUsedLines-1].content[ConsoleLines[NbrUsedLines-1].length-cursorPos], CONSOLE_LINELENGTH);
589 
590       strncat(begin, end, CONSOLE_LINELENGTH);
591       ConsoleLines[NbrUsedLines-1].SetContent(begin);
592     }
593   }
594   else
595   {
596     if (ConsoleLines[NbrUsedLines-1].length)
597     {
598       ConsoleLines[NbrUsedLines-1].content[ConsoleLines[NbrUsedLines-1].length-1] = '\0';
599       --ConsoleLines[NbrUsedLines-1].length;
600     }
601   }
602 }
603 
604 // R�initialise la commande
ReInitCurrentCommand(void)605 void Console::ReInitCurrentCommand(void)
606 {
607   cursorPos = 0;
608   ConsoleLines[NbrUsedLines-1].ReInit();
609 }
610 
611 // Retourne la commande
GetCurrentCommand(void)612 char* Console::GetCurrentCommand(void)
613 {
614   return ConsoleLines[NbrUsedLines-1].content;
615 }
616 
617 // D�finit la commande courante
SetCurrentCommand(char * s,...)618 void Console::SetCurrentCommand(char* s, ...)
619 {
620   va_list msg;
621   char buffer[CONSOLE_LINELENGTH] = { '\0' };
622 
623   va_start (msg, s);
624   _vsntprintf (buffer, CONSOLE_LINELENGTH - 1, s, msg);
625   va_end (msg);
626 
627   ConsoleLines[NbrUsedLines-1].SetContent(buffer);
628 }
629 
630 // Create a new line in console
AddLine(void)631 void Console::AddLine(void)
632 {
633   // Rem : Allocation is performed by blocs of size LINESBLOC,
634   //       this avoids to do frequent reallocations
635   if (NbrUsedLines == NbrAllocatedLines)
636   {
637     ConsoleLines = (ConsLine*) cake_realloc(ConsoleLines, (NbrAllocatedLines+LINESBLOC)*sizeof(ConsLine), "Console::AddLine.ConsoleLines");
638     if (!ConsoleLines) ThrowException(ALLOCATION_ERROR, "Console::AddLine.ConsoleLines");
639     for (int i = NbrAllocatedLines; i < NbrAllocatedLines+LINESBLOC; ++i) ConsoleLines[i].Init();
640     NbrAllocatedLines += LINESBLOC;
641   }
642   ++NbrUsedLines;
643   cursorPos = 0;
644 }
645 
646 // Add a line in messages list
AddMessageLine(char * s)647 void Console::AddMessageLine(char* s)
648 {
649   if (currently_used_message_lines < C_NMESSAGELINES) ++currently_used_message_lines;
650   else
651   {
652     for (int i = 0; i < currently_used_message_lines; ++i)
653     {
654       memset(MessageLines[i], '\0', CONSOLE_LINELENGTH);
655       strncpy(MessageLines[i], MessageLines[i+1], CONSOLE_LINELENGTH);
656       MessageLife[i] = MessageLife[i+1];
657     }
658   }
659 
660   if (autoCut)
661   {
662     // Count length of text invisible elements (color italic, bold tags)
663     unsigned long acceptable_len = (unsigned long) ((float)gRender->GetWidth()/(float)MiniConsoleFontSizeX);
664     unsigned long real_pos = 0, llen = 0;
665     while (llen < acceptable_len && real_pos < strlen(s))
666     {
667       if (s[real_pos] == '^') ++real_pos;
668       else ++llen;
669       ++real_pos;
670     }
671 
672     if (strlen(s) > real_pos)
673     {
674       // Search for a breaking position in string
675       while (real_pos > 0 && s[real_pos] != 32) --real_pos;
676       if (real_pos > 0)
677       {
678         char tmp_buff[CONSOLE_LINELENGTH] = { '\0' };
679         strncpy(tmp_buff, s, real_pos);
680 
681         MessageLife[currently_used_message_lines-1] = 0.f;
682         strcpy(MessageLines[currently_used_message_lines-1], tmp_buff);
683 
684         // Following variable is used to recopy the font parameters on next lines
685         char settings_s[CONSOLE_LINELENGTH] = { '\0' };
686         for (unsigned long i = 0; i < real_pos; ++i)
687         {
688           if (s[i] == '^')
689           {
690             settings_s[strlen(settings_s)] = s[i++];
691             settings_s[strlen(settings_s)] = s[i++];
692           }
693         }
694         strcat(settings_s, &s[real_pos+1]);
695         AddMessageLine(settings_s);
696         return;
697       }
698     }
699   }
700 
701   MessageLife[currently_used_message_lines-1] = 0.f;
702   sprintf(MessageLines[currently_used_message_lines-1], "%s^0", s);
703   startNewMessageLine = true;
704 }
705 
AddToLastMessageLine(char * s)706 void Console::AddToLastMessageLine(char *s)
707 {
708   if (currently_used_message_lines == 0 || startNewMessageLine)
709   {
710     AddMessageLine(s);
711     startNewMessageLine = false;
712     return;
713   }
714 
715   int l;
716   char buffer[CONSOLE_LINELENGTH] = { '\0' };
717   strncpy(buffer, MessageLines[currently_used_message_lines-1], CONSOLE_LINELENGTH);
718   if ((l = (int) strlen(buffer)) < CONSOLE_LINELENGTH-1) strncat(buffer, s, CONSOLE_LINELENGTH-l-1);
719 
720   if (autoCut)
721   {
722     // Count length of text invisible elements (color italic, bold tags)
723     unsigned long acceptable_len = (unsigned long) ((float)gRender->GetWidth()/(float)MiniConsoleFontSizeX);
724     unsigned long real_pos = 0, llen = 0;
725     while (llen < acceptable_len && real_pos < strlen(buffer))
726     {
727       if (buffer[real_pos] == '^') ++real_pos;
728       else ++llen;
729       ++real_pos;
730     }
731 
732     if (strlen(buffer) > real_pos)
733     {
734       // Search for a breaking position in string
735       while (real_pos > 0 && buffer[real_pos] != 32) --real_pos;
736       if (real_pos > 0)
737       {
738         char tmp_buff[CONSOLE_LINELENGTH] = { '\0' };
739         strncpy(tmp_buff, buffer, real_pos);
740 
741         MessageLife[currently_used_message_lines-1] = 0.f;
742         strcpy(MessageLines[currently_used_message_lines-1], tmp_buff);
743 
744         // Following variable is used to recopy the font parameters on next lines
745         char settings_s[CONSOLE_LINELENGTH] = { '\0' };
746         for (unsigned long i = 0; i < real_pos; ++i)
747         {
748           if (buffer[i] == '^')
749           {
750             settings_s[strlen(settings_s)] = buffer[i++];
751             settings_s[strlen(settings_s)] = buffer[i++];
752           }
753         }
754         strcat(settings_s, &buffer[real_pos+1]);
755         AddMessageLine(settings_s);
756         startNewMessageLine = false;
757         return;
758       }
759     }
760   }
761 
762   MessageLife[currently_used_message_lines-1] = 0.f;
763   strncpy(MessageLines[currently_used_message_lines-1], buffer, CONSOLE_LINELENGTH);
764 }
765 
Render(void)766 void Console::Render(void)
767 {
768 
769   if (state != CLOSED)
770   {
771     gRender->InitializeViewPort();        // set up 2d viewport
772 
773     // Draw the console background + text
774     gOver->Render(&shaders);          // render overlay
775     gRender->ForceFlush();
776 
777     // Draw the console border
778     GLboolean lighting, texture;
779 
780     glGetBooleanv(GL_TEXTURE_2D, &texture);
781     glGetBooleanv(GL_LIGHTING, &lighting);
782     glDisable(GL_TEXTURE_2D);
783     glDisable(GL_LIGHTING);
784 
785     State::setPolygonMode(GL_LINE);
786 
787     glColor4fv(ActiveBorderColor);
788     glLineWidth(2);
789     glBegin(GL_LINES);
790       glVertex2f(0, opening_or_closing_progress*(float)(topPos+height));
791       glVertex2f((float) width, opening_or_closing_progress*(float)(topPos+height));
792     glEnd();
793     glLineWidth(1);
794 
795     State::setPolygonMode(GL_FILL);
796 
797     gRender->CheckGLError(11);
798     if (texture) { glEnable(GL_TEXTURE_2D); gRender->CheckGLError(); }
799     if (lighting) { glEnable(GL_LIGHTING); gRender->CheckGLError(); }
800   }
801 }
802 
Update(void)803 void Console::Update(void)
804 {
805   // Update messages life time
806   for (cons_i = 0; cons_i < currently_used_message_lines; ++cons_i)
807   {
808     MessageLife[cons_i] += (float) Timer::frametime;
809   }
810 
811   for (cons_i = 0; cons_i < currently_used_message_lines; ++cons_i)
812   {
813     // Remove too old messages
814     if (MessageLife[cons_i] >= messageMaxLife)
815     {
816       for (cons_j = cons_i; cons_j < currently_used_message_lines; ++cons_j)
817       {
818         memset(MessageLines[cons_j], '\0', CONSOLE_LINELENGTH);
819         strncpy(MessageLines[cons_j], MessageLines[cons_j+1], CONSOLE_LINELENGTH);
820         MessageLife[cons_j] = MessageLife[cons_j+1];
821       }
822       memset(MessageLines[--currently_used_message_lines], '\0', CONSOLE_LINELENGTH);
823       --cons_i;
824     }
825     if (!currently_used_message_lines || cons_i < 0) break;
826   }
827 
828   // Display messages if console is collapsed
829   if ((state == CLOSED) && showMessages && currently_used_message_lines)
830   {
831     // Display messages only
832     for (cons_i = 0; cons_i < currently_used_message_lines; ++cons_i)
833     {
834       gOver->String(MessageLines[cons_i],
835         font,
836         0,
837         (float) (cons_i*MiniConsoleFontSizeY),
838         (float) MiniConsoleFontSizeX,
839         (float) MiniConsoleFontSizeY,
840         (float) fontRows,
841         (float) fontCols);
842     }
843   }
844 
845   if (state == CLOSED) return;
846 
847   char tmp_line[CONSOLE_LINELENGTH] = { '\0' };
848 
849   // Opening/Closing animation
850   if (!movingConsole && enableOpeningClosingAnimations)
851   {
852     if (state == OPENING) opening_or_closing_progress += (float)Timer::frametime/openSpeed;
853     if (state == CLOSING) opening_or_closing_progress -= (float)Timer::frametime/closeSpeed;
854     if (opening_or_closing_progress >= 1.f && state == OPENING) { state = OPEN; opening_or_closing_progress = 1.f; }
855     if (opening_or_closing_progress <= 0.f && state == CLOSING) { state = CLOSED; opening_or_closing_progress = 0.f;}
856   }
857   else
858   {
859     if (state == OPENING) { state = OPEN; opening_or_closing_progress = 1.f; }
860     if (state == CLOSING) { state = CLOSED; opening_or_closing_progress = 0.f;}
861   }
862 
863   // Draw background
864   if (back >= 0)
865   {
866     gOver->Quad(back, (float) leftPos, opening_or_closing_progress*(float)(topPos+height)-height, (float) width, (float) height);
867     char version[64] = { '\0' };
868     sprintf(version, "^2%s", _VERSION_);
869     #ifdef _DEBUG
870       strcat(version, " (debug)");
871     #else
872       strcat(version, " (release)");
873     #endif
874     gOver->String(version, GetFont(), (float)(width-ConsoleFontSizeX*(strlen(version)-2)), opening_or_closing_progress*(float)(topPos+height) - 1.5f*ConsoleFontSizeY);
875   }
876 
877   // Display console lines starting from bottom
878   if (NbrUsedLines < NbrMaxLines) cons_j = NbrUsedLines - 1;
879   else cons_j = NbrMaxLines - 1;
880   int pos_j = topPos+(int)((height+topPos)*(opening_or_closing_progress))-height+(cons_j)*ConsoleFontSizeY;
881 
882   // Display the command line
883   cons_i = NbrUsedLines - 1;
884   if ((long) ConsoleLines[cons_i].length < MaxTextLineLength - (prompt_len+1))
885   {
886     sprintf(tmp_line, "^0%s%s", prompt, ConsoleLines[cons_i].content);
887     gOver->String(tmp_line, font, (float) leftPos, (float) pos_j, (float) ConsoleFontSizeX, (float) ConsoleFontSizeY, (float) fontRows, (float) fontCols);
888   }
889   else
890   {
891     char buffer[CONSOLE_LINELENGTH] = { '\0' };
892 
893     if ((int) ConsoleLines[cons_i].length > cursorPos + MaxTextLineLength - (prompt_len+1))
894       strncpy(buffer, &(ConsoleLines[cons_i].content[ConsoleLines[cons_i].length-cursorPos-(MaxTextLineLength - (prompt_len+1))]), MaxTextLineLength-(prompt_len+1));
895     else
896       strncpy(buffer, ConsoleLines[cons_i].content, MaxTextLineLength - (prompt_len+1));
897 
898     sprintf(tmp_line, "^0%s%s", prompt, buffer);
899     gOver->String(tmp_line, font, (float) leftPos, (float) pos_j, (float) ConsoleFontSizeX, (float) ConsoleFontSizeY, (float) fontRows, (float) fontCols);
900   }
901 
902   // Cursor
903   if ((long)(Timer::fTime*cursorSpeed)%2) //  -> effet de clignotement du curseur
904   {
905     sprintf(tmp_line, "^0%c", cursorSymbol);
906     if ((long) ConsoleLines[NbrUsedLines - 1].length < MaxTextLineLength - prompt_len - 1)
907     {
908       gOver->String(tmp_line, font, (float) (leftPos+ConsoleFontSizeX*(ConsoleLines[NbrUsedLines-1].length+prompt_len-cursorPos)),
909         (float) pos_j, (float) ConsoleFontSizeX, (float) ConsoleFontSizeY, (float) fontRows, (float) fontCols);
910     }
911     else
912     {
913       if ((int) ConsoleLines[NbrUsedLines - 1].length > cursorPos + MaxTextLineLength - prompt_len - 1)
914       {
915         gOver->String(tmp_line, font, (float) (leftPos+ConsoleFontSizeX*(MaxTextLineLength-1)), (float) pos_j, (float) ConsoleFontSizeX, (float) ConsoleFontSizeY, (float) fontRows, (float) fontCols);
916       }
917       else
918       {
919         gOver->String(tmp_line, font, (float) (leftPos+ConsoleFontSizeX*(ConsoleLines[NbrUsedLines-1].length-cursorPos+prompt_len)),
920           (float) pos_j, (float) ConsoleFontSizeX, (float) ConsoleFontSizeY, (float) fontRows, (float) fontCols);
921       }
922     }
923   }
924 
925   pos_j -= ConsoleFontSizeY;
926   --cons_j;
927   --cons_i;
928 
929   // Other lines
930   int t = scrollVal;
931   while (cons_j >= 0 && cons_i >= 0)
932   {
933     for (int k = ConsoleLines[cons_i].nlines - 1; k >= 0; --k)
934     {
935       if (t <= 0)
936       {
937         gOver->String(ConsoleLines[cons_i].GetContent(k), font, (float) leftPos, (float) pos_j,
938            (float) ConsoleFontSizeX, (float) ConsoleFontSizeY, (float) fontRows, (float) fontCols);
939 
940         pos_j -= ConsoleFontSizeY;
941         --cons_j;
942       }
943       else --t;
944       if (cons_j < 0) break;
945     }
946     --cons_i;
947   }
948 }
949 
Recalculate_NLines(void)950 void Console::Recalculate_NLines(void)
951 {
952   NbrMaxLines = height/ConsoleFontSizeY;
953 
954   // Calculates the real number of used lines
955   NbrTrueLines = 1;
956   MaxTextLineLength = (int) ((float)width/(float)ConsoleFontSizeX);
957   for (int i = 0; i < NbrUsedLines - 1; ++i)
958     NbrTrueLines += ConsoleLines[i].Update(MaxTextLineLength);
959 }
960 
961 // Title, prompt
SetTitle(char * t)962 void Console::SetTitle(char *t)
963 {
964   memset(title, '\0', CONSOLE_LINELENGTH);
965   strncpy(title, t, CONSOLE_LINELENGTH);
966 }
967 
SetPrompt(char * s)968 void Console::SetPrompt (char *s)
969 {
970   if (prompt) cake_free(prompt);
971 
972   bool must_readapt_len = false;
973 
974   if (strlen(s) > MAXPROMPTLEN)
975   {
976     must_readapt_len = true;
977     while (strlen(s) > MAXPROMPTLEN) ++s;
978   }
979 
980   prompt_len = (int) (strlen(s)>MAXPROMPTLEN?MAXPROMPTLEN:strlen(s));
981   prompt = (char*) cake_malloc((prompt_len+1)*sizeof(char), "Console::SetPrompt.prompt");
982   memset(prompt, '\0', prompt_len+1);
983   strcpy(prompt, s);
984 
985   if (must_readapt_len)
986     prompt[0] = prompt[1] = prompt[2] = '.';
987 
988   prompt_len = (int) strlen(prompt);
989 }
990 
GetPrompt(void)991 char* Console::GetPrompt(void)
992 {
993   return prompt;
994 }
995 
996 // Opening and closing
Open(void)997 void Console::Open(void)
998 {
999   if (enableOpeningClosingAnimations) state = OPENING;
1000   else state = OPEN;
1001   isActive = true;
1002 }
1003 
Close(void)1004 void Console::Close(void)
1005 {
1006   if (enableOpeningClosingAnimations) state = CLOSING;
1007   else state = CLOSED;
1008   isActive = false;
1009 }
1010 
GetState(void)1011 enum_ConsoleStates Console::GetState(void)
1012 {
1013   return state;
1014 }
1015 
ToggleState(void)1016 enum_ConsoleStates Console::ToggleState(void)
1017 {
1018   if (state == CLOSING || state == CLOSED) Open();
1019   else Close();
1020 
1021   return state;
1022 }
1023 
SetState(enum_ConsoleStates s)1024 void Console::SetState(enum_ConsoleStates s)
1025 {
1026   state = s;
1027 
1028   if (!enableOpeningClosingAnimations)
1029   {
1030     if (state == OPENING) state = OPEN;
1031     if (state == CLOSING) state = CLOSED;
1032   }
1033 
1034   if (state == OPEN)
1035   {
1036     opening_or_closing_progress = 1.f;
1037     isActive = true;
1038   }
1039   else if (state == CLOSED)
1040   {
1041     opening_or_closing_progress = 0.f;
1042     isActive = false;
1043   }
1044 }
1045 
1046 // Scrolling
ScrollConsole(enum_ConsoleScrollDir dir)1047 void Console::ScrollConsole(enum_ConsoleScrollDir dir)
1048 {
1049   Recalculate_NLines();
1050   if (NbrTrueLines < NbrMaxLines)
1051   {
1052     beep();
1053     return;
1054   }
1055 
1056   if (dir == UP)
1057   {
1058     if ((scrollVal + 1) > (NbrTrueLines - NbrMaxLines)) { beep(); return; }
1059     ++scrollVal;
1060   }
1061   else if (dir == DOWN)
1062   {
1063     if ((scrollVal - 1) < 0) { beep(); return; }
1064     --scrollVal;
1065   }
1066   else if (dir == TOP)  scrollVal = NbrTrueLines - NbrMaxLines;
1067   else if (dir == BOTTOM) scrollVal = 0;
1068 }
1069 
SetVScrollYPos(int y,bool center)1070 void Console::SetVScrollYPos (int y, bool center)
1071 {
1072   scrollVal = (int) ((float)(center?y-RESIZE_SQUARE/2:y)*(float)(NbrMaxLines-NbrUsedLines)/
1073              (float)(height-RESIZE_SQUARE))+(int)((float)(NbrUsedLines-NbrMaxLines)*
1074              (float)(topPos+height-RESIZE_SQUARE)/(float)(height-RESIZE_SQUARE));
1075   if (scrollVal < 0) scrollVal = 0;
1076   if (scrollVal > NbrUsedLines - NbrMaxLines) scrollVal = NbrUsedLines - NbrMaxLines;
1077   if (NbrUsedLines < NbrMaxLines) scrollVal = 0;
1078 }
1079 
1080 // Type management
ToggleType(void)1081 void Console::ToggleType (void)
1082 {
1083   movingConsole = !movingConsole;
1084 }
1085 
1086 // Change d'�tat de console et prend automatiquement en compte la maximisation
SetType(bool console_type)1087 void Console::SetType(bool console_type)
1088 {
1089   if (movingConsole && !console_type)
1090   {
1091     if (!ConsoleIsMaximized)
1092     {
1093       HBackup = height;
1094       WBackup = width;
1095       LBackup = leftPos;
1096       TBackup = topPos;
1097     }
1098 
1099     topPos = 0;
1100     leftPos = 0;
1101     Resize(gRender->GetWidth(), gRender->GetHeight()/2);
1102 
1103     movingConsole = console_type;
1104   }
1105   else if (!movingConsole && console_type)
1106   {
1107     movingConsole = console_type;
1108 
1109     if (ConsoleIsMaximized)
1110     {
1111       topPos = titleBarHeight;
1112       leftPos = 0;
1113       Resize(gRender->GetWidth(), gRender->GetHeight()-titleBarHeight);
1114     }
1115     else
1116     {
1117       topPos = TBackup;
1118       leftPos = LBackup;
1119       Resize(WBackup, HBackup);
1120     }
1121   }
1122 }
1123 
1124 // Resizing
Resize(int w,int h)1125 void Console::Resize(int w, int h)
1126 {
1127   if (w > 0 &&
1128     (showVScroll && (w-RESIZE_SQUARE) > (prompt_len>MAXPROMPTLEN?prompt_len:MAXPROMPTLEN+2)*ConsoleFontSizeX+2) ||
1129     !showVScroll && w > (prompt_len>MAXPROMPTLEN?prompt_len:MAXPROMPTLEN+2)*ConsoleFontSizeX+2)
1130   {
1131     width = w;
1132   //  if (BackGround != NULL) BackGround->SetSize(width, -1);
1133   }
1134 
1135   if (h > 0 && h > 3*ConsoleFontSizeY)
1136   {
1137     height = h;
1138     Recalculate_NLines();
1139     if (scrollVal > (NbrUsedLines - NbrMaxLines)) scrollVal = (NbrUsedLines - NbrMaxLines);
1140     if (scrollVal < 0) scrollVal = 0;
1141   //  if (BackGround != NULL) BackGround->SetSize(-1, height);
1142   }
1143 }
1144 
Maximize(void)1145 void Console::Maximize(void)
1146 {
1147   if (ConsoleIsMaximized || !movingConsole) return;
1148 
1149   HBackup = height;
1150   WBackup = width;
1151   LBackup = leftPos;
1152   TBackup = topPos;
1153 
1154   topPos = titleBarHeight;
1155   leftPos = 0;
1156   Resize(gRender->GetWidth(), gRender->GetHeight()-titleBarHeight);
1157 
1158   ConsoleIsMaximized = true;
1159 }
1160 
Unmaximize(void)1161 void Console::Unmaximize(void)
1162 {
1163   if (!ConsoleIsMaximized || !movingConsole) return;
1164 
1165   ConsoleIsMaximized = false;
1166 
1167   topPos = TBackup;
1168   leftPos = LBackup;
1169   Resize(WBackup, HBackup);
1170 }
1171 
IsMaximized(void)1172 bool Console::IsMaximized(void)
1173 {
1174   return ConsoleIsMaximized;
1175 }
1176 
ToggleMaximisation(void)1177 void Console::ToggleMaximisation (void)
1178 {
1179   if (ConsoleIsMaximized) Unmaximize();
1180   else Maximize();
1181 }
1182 
1183 // Position management
SetTopPos(int top,int test)1184 void Console::SetTopPos(int top, int test)
1185 {
1186   if (test >= 1 && (ConsoleIsMaximized || !movingConsole)) return;
1187 
1188   topPos = top;
1189 
1190   if (test >= 0 && test <= 1)
1191   {
1192     if (topPos < 0) topPos = 0;
1193     if ((topPos + height) > gRender->GetHeight()) topPos = gRender->GetHeight() - height;
1194   }
1195 }
1196 
1197 // D�finit la position de gauche de la console
SetLeftPos(int left,int test)1198 void Console::SetLeftPos(int left, int test)
1199 {
1200   if (test >= 1 && (ConsoleIsMaximized || !movingConsole)) return;
1201 
1202   leftPos = left;
1203 
1204   if (test >= 0 && test <= 1)
1205   {
1206     if (leftPos < 0) leftPos = 0;
1207     if ((leftPos + width) > gRender->GetWidth()) leftPos = gRender->GetWidth() - width;
1208   }
1209 }
1210 
GetWidth(void)1211 int Console::GetWidth(void)
1212 {
1213   return width;
1214 }
1215 
GetHeight(void)1216 int Console::GetHeight(void)
1217 {
1218   return height;
1219 }
1220 
GetLeft(void)1221 int Console::GetLeft(void)
1222 {
1223   return leftPos;
1224 }
1225 
GetTop(void)1226 int Console::GetTop(void)
1227 {
1228   return topPos;
1229 }
1230 
1231 // Cursor management
MoveCursor(enum_ConsoleCursorScroll d)1232 void Console::MoveCursor(enum_ConsoleCursorScroll d)
1233 {
1234   cursorPos -= d;
1235   if (cursorPos < 0)
1236   {
1237     cursorPos = 0;
1238     if (d != C_EXTREM_RIGHT) beep();
1239   }
1240   if (cursorPos >= (int) ConsoleLines[NbrUsedLines-1].length)
1241   {
1242     cursorPos = (int) ConsoleLines[NbrUsedLines-1].length;
1243     if (d != C_EXTREM_LEFT) beep();
1244   }
1245 }
1246 
SetCursorSpeed(float f)1247 void Console::SetCursorSpeed(float f)
1248 {
1249   cursorSpeed = f;
1250 }
1251 
SetCursorSymbol(char c)1252 void Console::SetCursorSymbol(char c)
1253 {
1254   cursorSymbol = c;
1255 }
1256 
1257 // Font resizing
SetFontSize(int xsize,int ysize,int n)1258 void Console::SetFontSize (int xsize, int ysize, int n)
1259 {
1260   if (xsize < 1 && ysize < 1) return;
1261 
1262   if (n == 1)
1263   {
1264     if (xsize > 0) ConsoleFontSizeX = xsize;
1265     if (ysize > 0) ConsoleFontSizeY = ysize;
1266   }
1267   else if (n == 2)
1268   {
1269     if (xsize > 0) MiniConsoleFontSizeX = xsize;
1270     if (ysize > 0) MiniConsoleFontSizeY = ysize;
1271   }
1272   else
1273   {
1274     if (xsize > 0) ConsoleFontSizeX = MiniConsoleFontSizeX = xsize;
1275     if (ysize > 0) ConsoleFontSizeY = MiniConsoleFontSizeY = ysize;
1276   }
1277 
1278   titleBarHeight = 3*ConsoleFontSizeY/2;
1279   if (titleBarHeight <= RESIZE_SQUARE) titleBarHeight = RESIZE_SQUARE+RESIZE_SQUARE/2;
1280 
1281   //Recalculate_NLines();
1282 }
1283 
GetFontSizeX(int n)1284 int Console::GetFontSizeX(int n)
1285 {
1286   if (n == 2) return MiniConsoleFontSizeX;
1287   return ConsoleFontSizeX;
1288 }
1289 
GetFontSizeY(int n)1290 int Console::GetFontSizeY(int n)
1291 {
1292   if (n == 2) return MiniConsoleFontSizeY;
1293   return ConsoleFontSizeY;
1294 }
1295 
SetCoeff(float c)1296 void Console::SetCoeff(float c)
1297 {
1298   Coeff = c;
1299 }
1300 
1301 // Console Shaders
SetBack(int b)1302 void Console::SetBack(int b)    { back = b; }
SetClose(int c)1303 void Console::SetClose(int c)   { close = c; }
SetMaximise(int m)1304 void Console::SetMaximise(int m)  { maximise = m; }
SetReduce(int r)1305 void Console::SetReduce(int r)    { reduce = r; }
SetTitleBar(int t)1306 void Console::SetTitleBar(int t)  { titlebar = t; }
SetScrollUp(int s)1307 void Console::SetScrollUp(int s)  { scrollUp = s; }
SetScrollDown(int s)1308 void Console::SetScrollDown(int s)  { scrollDown = s; }
SetScroll(int s)1309 void Console::SetScroll(int s)    { scroll = s; }
SetResize(int r)1310 void Console::SetResize(int r)    { resize = r; }
1311 
SetFont(int f,int rows,int cols)1312 void Console::SetFont(int f, int rows, int cols)
1313 {
1314   font = f;
1315   if (rows > 0) fontRows = rows;
1316   if (cols > 0) fontCols = cols;
1317 }
1318 
GetFont(void)1319 int Console::GetFont(void)      { return font; }
1320 
1321