console.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. // console.c
  16. #include "client.h"
  17. console_t con;
  18. cvar_t *con_notifytime;
  19. #define MAXCMDLINE 256
  20. extern char key_lines[32][MAXCMDLINE];
  21. extern int edit_line;
  22. extern int key_linepos;
  23. void DrawString (int x, int y, char *s)
  24. {
  25. while (*s)
  26. {
  27. re.DrawChar (x, y, *s);
  28. x+=8;
  29. s++;
  30. }
  31. }
  32. void DrawAltString (int x, int y, char *s)
  33. {
  34. while (*s)
  35. {
  36. re.DrawChar (x, y, *s ^ 0x80);
  37. x+=8;
  38. s++;
  39. }
  40. }
  41. void Key_ClearTyping (void)
  42. {
  43. key_lines[edit_line][1] = 0; // clear any typing
  44. key_linepos = 1;
  45. }
  46. /*
  47. ================
  48. Con_ToggleConsole_f
  49. ================
  50. */
  51. void Con_ToggleConsole_f (void)
  52. {
  53. SCR_EndLoadingPlaque (); // get rid of loading plaque
  54. if (cl.attractloop)
  55. {
  56. Cbuf_AddText ("killserver\n");
  57. return;
  58. }
  59. if (cls.state == ca_disconnected)
  60. { // start the demo loop again
  61. Cbuf_AddText ("d1\n");
  62. return;
  63. }
  64. Key_ClearTyping ();
  65. Con_ClearNotify ();
  66. if (cls.key_dest == key_console)
  67. {
  68. M_ForceMenuOff ();
  69. Cvar_Set ("paused", "0");
  70. }
  71. else
  72. {
  73. M_ForceMenuOff ();
  74. cls.key_dest = key_console;
  75. if (Cvar_VariableValue ("maxclients") == 1
  76. && Com_ServerState ())
  77. Cvar_Set ("paused", "1");
  78. }
  79. }
  80. /*
  81. ================
  82. Con_ToggleChat_f
  83. ================
  84. */
  85. void Con_ToggleChat_f (void)
  86. {
  87. Key_ClearTyping ();
  88. if (cls.key_dest == key_console)
  89. {
  90. if (cls.state == ca_active)
  91. {
  92. M_ForceMenuOff ();
  93. cls.key_dest = key_game;
  94. }
  95. }
  96. else
  97. cls.key_dest = key_console;
  98. Con_ClearNotify ();
  99. }
  100. /*
  101. ================
  102. Con_Clear_f
  103. ================
  104. */
  105. void Con_Clear_f (void)
  106. {
  107. memset (con.text, ' ', CON_TEXTSIZE);
  108. }
  109. /*
  110. ================
  111. Con_Dump_f
  112. Save the console contents out to a file
  113. ================
  114. */
  115. void Con_Dump_f (void)
  116. {
  117. int l, x;
  118. char *line;
  119. FILE *f;
  120. char buffer[1024];
  121. char name[MAX_OSPATH];
  122. if (Cmd_Argc() != 2)
  123. {
  124. Com_Printf ("usage: condump <filename>\n");
  125. return;
  126. }
  127. Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1));
  128. Com_Printf ("Dumped console text to %s.\n", name);
  129. FS_CreatePath (name);
  130. f = fopen (name, "w");
  131. if (!f)
  132. {
  133. Com_Printf ("ERROR: couldn't open.\n");
  134. return;
  135. }
  136. // skip empty lines
  137. for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
  138. {
  139. line = con.text + (l%con.totallines)*con.linewidth;
  140. for (x=0 ; x<con.linewidth ; x++)
  141. if (line[x] != ' ')
  142. break;
  143. if (x != con.linewidth)
  144. break;
  145. }
  146. // write the remaining lines
  147. buffer[con.linewidth] = 0;
  148. for ( ; l <= con.current ; l++)
  149. {
  150. line = con.text + (l%con.totallines)*con.linewidth;
  151. strncpy (buffer, line, con.linewidth);
  152. for (x=con.linewidth-1 ; x>=0 ; x--)
  153. {
  154. if (buffer[x] == ' ')
  155. buffer[x] = 0;
  156. else
  157. break;
  158. }
  159. for (x=0; buffer[x]; x++)
  160. buffer[x] &= 0x7f;
  161. fprintf (f, "%s\n", buffer);
  162. }
  163. fclose (f);
  164. }
  165. /*
  166. ================
  167. Con_ClearNotify
  168. ================
  169. */
  170. void Con_ClearNotify (void)
  171. {
  172. int i;
  173. for (i=0 ; i<NUM_CON_TIMES ; i++)
  174. con.times[i] = 0;
  175. }
  176. /*
  177. ================
  178. Con_MessageMode_f
  179. ================
  180. */
  181. void Con_MessageMode_f (void)
  182. {
  183. chat_team = false;
  184. cls.key_dest = key_message;
  185. }
  186. /*
  187. ================
  188. Con_MessageMode2_f
  189. ================
  190. */
  191. void Con_MessageMode2_f (void)
  192. {
  193. chat_team = true;
  194. cls.key_dest = key_message;
  195. }
  196. /*
  197. ================
  198. Con_CheckResize
  199. If the line width has changed, reformat the buffer.
  200. ================
  201. */
  202. void Con_CheckResize (void)
  203. {
  204. int i, j, width, oldwidth, oldtotallines, numlines, numchars;
  205. char tbuf[CON_TEXTSIZE];
  206. width = (viddef.width >> 3) - 2;
  207. if (width == con.linewidth)
  208. return;
  209. if (width < 1) // video hasn't been initialized yet
  210. {
  211. width = 38;
  212. con.linewidth = width;
  213. con.totallines = CON_TEXTSIZE / con.linewidth;
  214. memset (con.text, ' ', CON_TEXTSIZE);
  215. }
  216. else
  217. {
  218. oldwidth = con.linewidth;
  219. con.linewidth = width;
  220. oldtotallines = con.totallines;
  221. con.totallines = CON_TEXTSIZE / con.linewidth;
  222. numlines = oldtotallines;
  223. if (con.totallines < numlines)
  224. numlines = con.totallines;
  225. numchars = oldwidth;
  226. if (con.linewidth < numchars)
  227. numchars = con.linewidth;
  228. memcpy (tbuf, con.text, CON_TEXTSIZE);
  229. memset (con.text, ' ', CON_TEXTSIZE);
  230. for (i=0 ; i<numlines ; i++)
  231. {
  232. for (j=0 ; j<numchars ; j++)
  233. {
  234. con.text[(con.totallines - 1 - i) * con.linewidth + j] =
  235. tbuf[((con.current - i + oldtotallines) %
  236. oldtotallines) * oldwidth + j];
  237. }
  238. }
  239. Con_ClearNotify ();
  240. }
  241. con.current = con.totallines - 1;
  242. con.display = con.current;
  243. }
  244. /*
  245. ================
  246. Con_Init
  247. ================
  248. */
  249. void Con_Init (void)
  250. {
  251. con.linewidth = -1;
  252. Con_CheckResize ();
  253. Com_Printf ("Console initialized.\n");
  254. //
  255. // register our commands
  256. //
  257. con_notifytime = Cvar_Get ("con_notifytime", "3", 0);
  258. Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
  259. Cmd_AddCommand ("togglechat", Con_ToggleChat_f);
  260. Cmd_AddCommand ("messagemode", Con_MessageMode_f);
  261. Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
  262. Cmd_AddCommand ("clear", Con_Clear_f);
  263. Cmd_AddCommand ("condump", Con_Dump_f);
  264. con.initialized = true;
  265. }
  266. /*
  267. ===============
  268. Con_Linefeed
  269. ===============
  270. */
  271. void Con_Linefeed (void)
  272. {
  273. con.x = 0;
  274. if (con.display == con.current)
  275. con.display++;
  276. con.current++;
  277. memset (&con.text[(con.current%con.totallines)*con.linewidth]
  278. , ' ', con.linewidth);
  279. }
  280. /*
  281. ================
  282. Con_Print
  283. Handles cursor positioning, line wrapping, etc
  284. All console printing must go through this in order to be logged to disk
  285. If no console is visible, the text will appear at the top of the game window
  286. ================
  287. */
  288. void Con_Print (char *txt)
  289. {
  290. int y;
  291. int c, l;
  292. static int cr;
  293. int mask;
  294. if (!con.initialized)
  295. return;
  296. if (txt[0] == 1 || txt[0] == 2)
  297. {
  298. mask = 128; // go to colored text
  299. txt++;
  300. }
  301. else
  302. mask = 0;
  303. while ( (c = *txt) )
  304. {
  305. // count word length
  306. for (l=0 ; l< con.linewidth ; l++)
  307. if ( txt[l] <= ' ')
  308. break;
  309. // word wrap
  310. if (l != con.linewidth && (con.x + l > con.linewidth) )
  311. con.x = 0;
  312. txt++;
  313. if (cr)
  314. {
  315. con.current--;
  316. cr = false;
  317. }
  318. if (!con.x)
  319. {
  320. Con_Linefeed ();
  321. // mark time for transparent overlay
  322. if (con.current >= 0)
  323. con.times[con.current % NUM_CON_TIMES] = cls.realtime;
  324. }
  325. switch (c)
  326. {
  327. case '\n':
  328. con.x = 0;
  329. break;
  330. case '\r':
  331. con.x = 0;
  332. cr = 1;
  333. break;
  334. default: // display character and advance
  335. y = con.current % con.totallines;
  336. con.text[y*con.linewidth+con.x] = c | mask | con.ormask;
  337. con.x++;
  338. if (con.x >= con.linewidth)
  339. con.x = 0;
  340. break;
  341. }
  342. }
  343. }
  344. /*
  345. ==============
  346. Con_CenteredPrint
  347. ==============
  348. */
  349. void Con_CenteredPrint (char *text)
  350. {
  351. int l;
  352. char buffer[1024];
  353. l = strlen(text);
  354. l = (con.linewidth-l)/2;
  355. if (l < 0)
  356. l = 0;
  357. memset (buffer, ' ', l);
  358. strcpy (buffer+l, text);
  359. strcat (buffer, "\n");
  360. Con_Print (buffer);
  361. }
  362. /*
  363. ==============================================================================
  364. DRAWING
  365. ==============================================================================
  366. */
  367. /*
  368. ================
  369. Con_DrawInput
  370. The input line scrolls horizontally if typing goes beyond the right edge
  371. ================
  372. */
  373. void Con_DrawInput (void)
  374. {
  375. int y;
  376. int i;
  377. char *text;
  378. if (cls.key_dest == key_menu)
  379. return;
  380. if (cls.key_dest != key_console && cls.state == ca_active)
  381. return; // don't draw anything (always draw if not active)
  382. text = key_lines[edit_line];
  383. // add the cursor frame
  384. text[key_linepos] = 10+((int)(cls.realtime>>8)&1);
  385. // fill out remainder with spaces
  386. for (i=key_linepos+1 ; i< con.linewidth ; i++)
  387. text[i] = ' ';
  388. // prestep if horizontally scrolling
  389. if (key_linepos >= con.linewidth)
  390. text += 1 + key_linepos - con.linewidth;
  391. // draw it
  392. y = con.vislines-16;
  393. for (i=0 ; i<con.linewidth ; i++)
  394. re.DrawChar ( (i+1)<<3, con.vislines - 22, text[i]);
  395. // remove cursor
  396. key_lines[edit_line][key_linepos] = 0;
  397. }
  398. /*
  399. ================
  400. Con_DrawNotify
  401. Draws the last few lines of output transparently over the game top
  402. ================
  403. */
  404. void Con_DrawNotify (void)
  405. {
  406. int x, v;
  407. char *text;
  408. int i;
  409. int time;
  410. char *s;
  411. int skip;
  412. v = 0;
  413. for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
  414. {
  415. if (i < 0)
  416. continue;
  417. time = con.times[i % NUM_CON_TIMES];
  418. if (time == 0)
  419. continue;
  420. time = cls.realtime - time;
  421. if (time > con_notifytime->value*1000)
  422. continue;
  423. text = con.text + (i % con.totallines)*con.linewidth;
  424. for (x = 0 ; x < con.linewidth ; x++)
  425. re.DrawChar ( (x+1)<<3, v, text[x]);
  426. v += 8;
  427. }
  428. if (cls.key_dest == key_message)
  429. {
  430. if (chat_team)
  431. {
  432. DrawString (8, v, "say_team:");
  433. skip = 11;
  434. }
  435. else
  436. {
  437. DrawString (8, v, "say:");
  438. skip = 5;
  439. }
  440. s = chat_buffer;
  441. if (chat_bufferlen > (viddef.width>>3)-(skip+1))
  442. s += chat_bufferlen - ((viddef.width>>3)-(skip+1));
  443. x = 0;
  444. while(s[x])
  445. {
  446. re.DrawChar ( (x+skip)<<3, v, s[x]);
  447. x++;
  448. }
  449. re.DrawChar ( (x+skip)<<3, v, 10+((cls.realtime>>8)&1));
  450. v += 8;
  451. }
  452. if (v)
  453. {
  454. SCR_AddDirtyPoint (0,0);
  455. SCR_AddDirtyPoint (viddef.width-1, v);
  456. }
  457. }
  458. /*
  459. ================
  460. Con_DrawConsole
  461. Draws the console with the solid background
  462. ================
  463. */
  464. void Con_DrawConsole (float frac)
  465. {
  466. int i, j, x, y, n;
  467. int rows;
  468. char *text;
  469. int row;
  470. int lines;
  471. char version[64];
  472. char dlbar[1024];
  473. lines = viddef.height * frac;
  474. if (lines <= 0)
  475. return;
  476. if (lines > viddef.height)
  477. lines = viddef.height;
  478. // draw the background
  479. re.DrawStretchPic (0, -viddef.height+lines, viddef.width, viddef.height, "conback");
  480. SCR_AddDirtyPoint (0,0);
  481. SCR_AddDirtyPoint (viddef.width-1,lines-1);
  482. Com_sprintf (version, sizeof(version), "v%4.2f", VERSION);
  483. for (x=0 ; x<5 ; x++)
  484. re.DrawChar (viddef.width-44+x*8, lines-12, 128 + version[x] );
  485. // draw the text
  486. con.vislines = lines;
  487. #if 0
  488. rows = (lines-8)>>3; // rows of text to draw
  489. y = lines - 24;
  490. #else
  491. rows = (lines-22)>>3; // rows of text to draw
  492. y = lines - 30;
  493. #endif
  494. // draw from the bottom up
  495. if (con.display != con.current)
  496. {
  497. // draw arrows to show the buffer is backscrolled
  498. for (x=0 ; x<con.linewidth ; x+=4)
  499. re.DrawChar ( (x+1)<<3, y, '^');
  500. y -= 8;
  501. rows--;
  502. }
  503. row = con.display;
  504. for (i=0 ; i<rows ; i++, y-=8, row--)
  505. {
  506. if (row < 0)
  507. break;
  508. if (con.current - row >= con.totallines)
  509. break; // past scrollback wrap point
  510. text = con.text + (row % con.totallines)*con.linewidth;
  511. for (x=0 ; x<con.linewidth ; x++)
  512. re.DrawChar ( (x+1)<<3, y, text[x]);
  513. }
  514. //ZOID
  515. // draw the download bar
  516. // figure out width
  517. if (cls.download) {
  518. if ((text = strrchr(cls.downloadname, '/')) != NULL)
  519. text++;
  520. else
  521. text = cls.downloadname;
  522. x = con.linewidth - ((con.linewidth * 7) / 40);
  523. y = x - strlen(text) - 8;
  524. i = con.linewidth/3;
  525. if (strlen(text) > i) {
  526. y = x - i - 11;
  527. strncpy(dlbar, text, i);
  528. dlbar[i] = 0;
  529. strcat(dlbar, "...");
  530. } else
  531. strcpy(dlbar, text);
  532. strcat(dlbar, ": ");
  533. i = strlen(dlbar);
  534. dlbar[i++] = '\x80';
  535. // where's the dot go?
  536. if (cls.downloadpercent == 0)
  537. n = 0;
  538. else
  539. n = y * cls.downloadpercent / 100;
  540. for (j = 0; j < y; j++)
  541. if (j == n)
  542. dlbar[i++] = '\x83';
  543. else
  544. dlbar[i++] = '\x81';
  545. dlbar[i++] = '\x82';
  546. dlbar[i] = 0;
  547. sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
  548. // draw it
  549. y = con.vislines-12;
  550. for (i = 0; i < strlen(dlbar); i++)
  551. re.DrawChar ( (i+1)<<3, y, dlbar[i]);
  552. }
  553. //ZOID
  554. // draw the input prompt, user text, and cursor if desired
  555. Con_DrawInput ();
  556. }