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