sources/interface.cpp

changeset 140
e49aa4aa98c0
parent 139
da7d5a8e608f
child 141
d9073c13dc98
child 143
b9993733952a
equal deleted inserted replaced
139:da7d5a8e608f 140:e49aa4aa98c0
39 39
40 static const int PAGE_SIZE = 10; 40 static const int PAGE_SIZE = 10;
41 41
42 // ------------------------------------------------------------------------------------------------- 42 // -------------------------------------------------------------------------------------------------
43 // 43 //
44 chtype Interface::getColorPair (Color fg, Color bg) 44 chtype Interface::getColorPair(Color fg, Color bg)
45 { 45 {
46 if (fg == DEFAULT && bg == DEFAULT) 46 if (fg == DEFAULT && bg == DEFAULT)
47 return 0; 47 return 0;
48 else 48 else
49 return COLOR_PAIR (1 + (int (fg) * NUM_COLORS) + int (bg)); 49 return COLOR_PAIR(1 + (int(fg) * NUM_COLORS) + int(bg));
50 } 50 }
51 51
52 // ------------------------------------------------------------------------------------------------- 52 // -------------------------------------------------------------------------------------------------
53 // 53 //
54 const String& Interface::getCurrentInput() 54 const String& Interface::getCurrentInput()
56 return m_inputHistory[m_inputCursor]; 56 return m_inputHistory[m_inputCursor];
57 } 57 }
58 58
59 // ------------------------------------------------------------------------------------------------- 59 // -------------------------------------------------------------------------------------------------
60 // 60 //
61 // Makes current_input() the lastmost input (so that we won't modify history) 61 // Makes current_input() the lastmost input(so that we won't modify history)
62 // 62 //
63 void Interface::detachInput() 63 void Interface::detachInput()
64 { 64 {
65 if (m_inputCursor > 0) 65 if (m_inputCursor > 0)
66 { 66 {
78 return m_inputHistory[m_inputCursor]; 78 return m_inputHistory[m_inputCursor];
79 } 79 }
80 80
81 // ------------------------------------------------------------------------------------------------- 81 // -------------------------------------------------------------------------------------------------
82 // 82 //
83 void Interface::moveInputCursor (int delta) 83 void Interface::moveInputCursor(int delta)
84 { 84 {
85 // No input history when inputting addresses or passwords 85 // No input history when inputting addresses or passwords
86 if (m_inputState != INPUTSTATE_NORMAL) 86 if (m_inputState != INPUTSTATE_NORMAL)
87 { 87 {
88 m_inputCursor = 0; 88 m_inputCursor = 0;
89 return; 89 return;
90 } 90 }
91 91
92 int oldcursor = m_inputCursor; 92 int oldcursor = m_inputCursor;
93 m_inputCursor = clamp (m_inputCursor + delta, 0, m_inputHistory.size() - 1); 93 m_inputCursor = clamp(m_inputCursor + delta, 0, m_inputHistory.size() - 1);
94 94
95 if (m_inputCursor != oldcursor) 95 if (m_inputCursor != oldcursor)
96 { 96 {
97 m_cursorPosition = getCurrentInput().length(); 97 m_cursorPosition = getCurrentInput().length();
98 m_needInputRender = true; 98 m_needInputRender = true;
116 return prompt; 116 return prompt;
117 } 117 }
118 118
119 // ------------------------------------------------------------------------------------------------- 119 // -------------------------------------------------------------------------------------------------
120 // 120 //
121 void Interface::setInputState (InputState newstate) 121 void Interface::setInputState(InputState newstate)
122 { 122 {
123 // Clear the input row (unless going to or from confirm state) 123 // Clear the input row(unless going to or from confirm state)
124 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION 124 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION
125 and m_inputState != INPUTSTATE_CONFIRM_DISCONNECTION) 125 and m_inputState != INPUTSTATE_CONFIRM_DISCONNECTION)
126 { 126 {
127 m_inputCursor = 0; 127 m_inputCursor = 0;
128 getEditableInput().clear(); 128 getEditableInput().clear();
130 130
131 switch (newstate) 131 switch (newstate)
132 { 132 {
133 case INPUTSTATE_ADDRESS: 133 case INPUTSTATE_ADDRESS:
134 if (m_remoteAddress.host != 0) 134 if (m_remoteAddress.host != 0)
135 getEditableInput() = m_remoteAddress.to_string (IPAddress::WITH_PORT); 135 getEditableInput() = m_remoteAddress.to_string(IPAddress::WITH_PORT);
136 break; 136 break;
137 137
138 default: 138 default:
139 break; 139 break;
140 } 140 }
144 } 144 }
145 145
146 // ------------------------------------------------------------------------------------------------- 146 // -------------------------------------------------------------------------------------------------
147 // 147 //
148 Interface::Interface() : 148 Interface::Interface() :
149 m_inputCursor (0), 149 m_inputCursor(0),
150 m_cursorPosition (0), 150 m_cursorPosition(0),
151 m_inputPanning (0), 151 m_inputPanning(0),
152 m_needRefresh (false), 152 m_needRefresh(false),
153 m_needStatusBarRender (false), 153 m_needStatusBarRender(false),
154 m_needInputRender (false), 154 m_needInputRender(false),
155 m_needOutputRender (false), 155 m_needOutputRender(false),
156 m_needNicklistRender (false), 156 m_needNicklistRender(false),
157 m_outputScroll (0), 157 m_outputScroll(0),
158 m_inputState (INPUTSTATE_NORMAL), 158 m_inputState(INPUTSTATE_NORMAL),
159 m_disconnectCallback (nullptr) 159 m_disconnectCallback(nullptr)
160 { 160 {
161 ::initscr(); 161 ::initscr();
162 ::raw(); 162 ::raw();
163 ::keypad (stdscr, true); 163 ::keypad(stdscr, true);
164 ::noecho(); 164 ::noecho();
165 ::refresh(); 165 ::refresh();
166 ::timeout (0); 166 ::timeout(0);
167 m_inputHistory.clear(); 167 m_inputHistory.clear();
168 m_inputHistory << ""; 168 m_inputHistory << "";
169 m_outputLines.clear(); 169 m_outputLines.clear();
170 m_outputLines << ColoredLine(); 170 m_outputLines << ColoredLine();
171 m_session.set_interface (this); 171 m_session.set_interface(this);
172 resetTitle(); 172 resetTitle();
173 173
174 if (::has_colors()) 174 if (::has_colors())
175 { 175 {
176 ::start_color(); 176 ::start_color();
177 bool hasDefaultColors = (::use_default_colors() == OK); 177 bool hasDefaultColors =(::use_default_colors() == OK);
178 int defaultFg = hasDefaultColors ? -1 : COLOR_WHITE; 178 int defaultFg = hasDefaultColors ? -1 : COLOR_WHITE;
179 int defaultBg = hasDefaultColors ? -1 : COLOR_BLACK; 179 int defaultBg = hasDefaultColors ? -1 : COLOR_BLACK;
180 180
181 // Initialize color pairs 181 // Initialize color pairs
182 for (int i : range<int>(NUM_COLORS)) 182 for (int i : range<int>(NUM_COLORS))
183 for (int j : range<int>(NUM_COLORS)) 183 for (int j : range<int>(NUM_COLORS))
184 { 184 {
185 int pairnum = 1 + (i * NUM_COLORS + j); 185 int pairnum = 1 + (i * NUM_COLORS + j);
186 int fg = (i == DEFAULT) ? defaultFg : i; 186 int fg =(i == DEFAULT) ? defaultFg : i;
187 int bg = (j == DEFAULT) ? defaultBg : j; 187 int bg =(j == DEFAULT) ? defaultBg : j;
188 188
189 if (fg != -1 || bg != -1) 189 if (fg != -1 || bg != -1)
190 { 190 {
191 if (::init_pair (pairnum, fg, bg) == ERR) 191 if (::init_pair(pairnum, fg, bg) == ERR)
192 printWarning ("Unable to initialize color pair %d (%d, %d)\n", pairnum, fg, bg); 192 printWarning("Unable to initialize color pair %d(%d, %d)\n", pairnum, fg, bg);
193 } 193 }
194 } 194 }
195 } 195 }
196 196
197 renderFull(); 197 renderFull();
203 // 203 //
204 void Interface::renderTitlebar() 204 void Interface::renderTitlebar()
205 { 205 {
206 if (m_title.length() <= COLS) 206 if (m_title.length() <= COLS)
207 { 207 {
208 chtype pair = getColorPair (WHITE, BLUE); 208 chtype pair = getColorPair(WHITE, BLUE);
209 int startx = (COLS - m_title.length()) / 2; 209 int startx =(COLS - m_title.length()) / 2;
210 int endx = startx + m_title.length(); 210 int endx = startx + m_title.length();
211 attron (pair); 211 attron(pair);
212 mvprintw (0, startx, "%s", m_title.chars()); 212 mvprintw(0, startx, "%s", m_title.chars());
213 mvhline (0, 0, ' ', startx); 213 mvhline(0, 0, ' ', startx);
214 mvhline (0, endx, ' ', COLS - endx); 214 mvhline(0, endx, ' ', COLS - endx);
215 attroff (pair); 215 attroff(pair);
216 } 216 }
217 217
218 m_needRefresh = true; 218 m_needRefresh = true;
219 } 219 }
220 220
221 // ------------------------------------------------------------------------------------------------- 221 // -------------------------------------------------------------------------------------------------
222 // 222 //
223 void Interface::setTitle (const String& title) 223 void Interface::setTitle(const String& title)
224 { 224 {
225 m_title = title; 225 m_title = title;
226 renderTitlebar(); 226 renderTitlebar();
227 } 227 }
228 228
229 // ------------------------------------------------------------------------------------------------- 229 // -------------------------------------------------------------------------------------------------
230 // 230 //
231 void Interface::safeDisconnect (std::function<void(bool)> afterwards) 231 void Interface::safeDisconnect(std::function<void(bool)> afterwards)
232 { 232 {
233 if (m_session.is_active()) 233 if (m_session.is_active())
234 { 234 {
235 m_disconnectCallback = afterwards; 235 m_disconnectCallback = afterwards;
236 setInputState (INPUTSTATE_CONFIRM_DISCONNECTION); 236 setInputState(INPUTSTATE_CONFIRM_DISCONNECTION);
237 } 237 }
238 else 238 else
239 afterwards(false); 239 afterwards(false);
240 } 240 }
241 241
242 // ------------------------------------------------------------------------------------------------- 242 // -------------------------------------------------------------------------------------------------
243 // 243 //
244 int Interface::nicklistWidth() 244 int Interface::nicklistWidth()
245 { 245 {
246 // Allocate at least 12 characters, at most 24 characters, for the nicklist. If we cannot 246 // Allocate at least 12 characters, at most 24 characters, for the nicklist. If we cannot
247 // afford that (o_O) then we probably shouldn't draw the nicklist at all I think. 247 // afford that(o_O) then we probably shouldn't draw the nicklist at all I think.
248 int nicklistWidth = COLS / 4; 248 int nicklistWidth = COLS / 4;
249 249
250 if (nicklistWidth < 12) 250 if (nicklistWidth < 12)
251 return 0; 251 return 0;
252 252
253 return min (nicklistWidth, 24); 253 return min(nicklistWidth, 24);
254 } 254 }
255 255
256 // ------------------------------------------------------------------------------------------------- 256 // -------------------------------------------------------------------------------------------------
257 // Renders the given colored line onto the screen. Will wrap if allowWrap is true. Returns the 257 // Renders the given colored line onto the screen. Will wrap if allowWrap is true. Returns the
258 // 'y' value for the next line. 258 // 'y' value for the next line.
259 // 259 //
260 int Interface::renderColorline (int y, int x0, int width, const ColoredLine& line, bool allowWrap) 260 int Interface::renderColorline(int y, int x0, int width, const ColoredLine& line, bool allowWrap)
261 { 261 {
262 int x = x0; 262 int x = x0;
263 263
264 for (int byte : line.data()) 264 for (int byte : line.data())
265 { 265 {
270 270
271 x = x0; 271 x = x0;
272 ++y; 272 ++y;
273 } 273 }
274 274
275 if (byte < 256 && isprint (byte)) 275 if (byte < 256 && isprint(byte))
276 { 276 {
277 mvaddch (y, x, char (byte)); 277 mvaddch(y, x, char(byte));
278 ++x; 278 ++x;
279 } 279 }
280 else if (byte >= RLINE_ON_COLOR and byte < (RLINE_ON_COLOR + 16)) 280 else if (byte >= RLINE_ON_COLOR and byte <(RLINE_ON_COLOR + 16))
281 { 281 {
282 auto attrfunction = (byte < RLINE_OFF_COLOR ? &attron : &attroff); 282 auto attrfunction =(byte < RLINE_OFF_COLOR ? &attron : &attroff);
283 (*attrfunction) (getColorPair (Color ((byte - RLINE_ON_COLOR) & 7), DEFAULT)); 283 (*attrfunction)(getColorPair(Color((byte - RLINE_ON_COLOR) & 7), DEFAULT));
284 } 284 }
285 else switch (byte) 285 else switch (byte)
286 { 286 {
287 case RLINE_ON_BOLD: 287 case RLINE_ON_BOLD:
288 attron (A_BOLD); 288 attron(A_BOLD);
289 break; 289 break;
290 290
291 case RLINE_OFF_BOLD: 291 case RLINE_OFF_BOLD:
292 attroff (A_BOLD); 292 attroff(A_BOLD);
293 break; 293 break;
294 } 294 }
295 } 295 }
296 296
297 return y + 1; 297 return y + 1;
302 void Interface::renderOutput() 302 void Interface::renderOutput()
303 { 303 {
304 if (m_outputLines.size() == 1) 304 if (m_outputLines.size() == 1)
305 return; 305 return;
306 306
307 m_outputScroll = clamp (m_outputScroll, 0, m_outputLines.size() - 1); 307 m_outputScroll = clamp(m_outputScroll, 0, m_outputLines.size() - 1);
308 308
309 int height = LINES - 3; 309 int height = LINES - 3;
310 int width = COLS - nicklistWidth(); 310 int width = COLS - nicklistWidth();
311 int printOffset = 0; 311 int printOffset = 0;
312 int end = m_outputLines.size() - 1 - m_outputScroll; 312 int end = m_outputLines.size() - 1 - m_outputScroll;
316 bool tightFit = false; 316 bool tightFit = false;
317 317
318 // Where to start? 318 // Where to start?
319 while (start > 0) 319 while (start > 0)
320 { 320 {
321 int rows = m_outputLines[start - 1].rows (width); 321 int rows = m_outputLines[start - 1].rows(width);
322 322
323 if (usedHeight + rows > height) 323 if (usedHeight + rows > height)
324 { 324 {
325 // This line won't fit anymore. 325 // This line won't fit anymore.
326 tightFit = true; 326 tightFit = true;
329 329
330 start--; 330 start--;
331 usedHeight += rows; 331 usedHeight += rows;
332 } 332 }
333 333
334 // See if there's any more rows to use (end may be too small) 334 // See if there's any more rows to use(end may be too small)
335 if (not tightFit) 335 if (not tightFit)
336 { 336 {
337 while (end < m_outputLines.size()) 337 while (end < m_outputLines.size())
338 { 338 {
339 int rows = m_outputLines[end].rows (width); 339 int rows = m_outputLines[end].rows(width);
340 340
341 if (usedHeight + rows > height) 341 if (usedHeight + rows > height)
342 { 342 {
343 tightFit = true; 343 tightFit = true;
344 break; 344 break;
355 m_outputScroll = m_outputLines.size() - 1 - end; 355 m_outputScroll = m_outputLines.size() - 1 - end;
356 356
357 if (start < 0 or start == end or printOffset >= height) 357 if (start < 0 or start == end or printOffset >= height)
358 return; 358 return;
359 359
360 assert (start <= end and start - end <= height); 360 assert(start <= end and start - end <= height);
361 361
362 // Clear the display 362 // Clear the display
363 for (int i : range(height)) 363 for (int i : range(height))
364 mvhline (y + i, 0, ' ', width); 364 mvhline(y + i, 0, ' ', width);
365 365
366 // Print the lines 366 // Print the lines
367 y += printOffset; 367 y += printOffset;
368 368
369 for (int i : range(start, end)) 369 for (int i : range(start, end))
370 y = renderColorline (y, 0, width, m_outputLines[i], true); 370 y = renderColorline(y, 0, width, m_outputLines[i], true);
371 371
372 m_needOutputRender = false; 372 m_needOutputRender = false;
373 m_needRefresh = true; 373 m_needRefresh = true;
374 } 374 }
375 375
385 if (width > 0) 385 if (width > 0)
386 return; 386 return;
387 387
388 for (int i : range(height)) 388 for (int i : range(height))
389 { 389 {
390 mvhline (y, x, ' ', width); 390 mvhline(y, x, ' ', width);
391 391
392 if (i < m_playerNames.size()) 392 if (i < m_playerNames.size())
393 renderColorline (y, x, width, m_playerNames[i], false); 393 renderColorline(y, x, width, m_playerNames[i], false);
394 394
395 y++; 395 y++;
396 } 396 }
397 397
398 m_needNicklistRender = false; 398 m_needNicklistRender = false;
401 401
402 // ------------------------------------------------------------------------------------------------- 402 // -------------------------------------------------------------------------------------------------
403 // 403 //
404 void Interface::renderInput() 404 void Interface::renderInput()
405 { 405 {
406 chtype promptColor = getColorPair (WHITE, BLUE); 406 chtype promptColor = getColorPair(WHITE, BLUE);
407 407
408 // If we're asking the user if they want to disconnect, we don't render any input strings, 408 // If we're asking the user if they want to disconnect, we don't render any input strings,
409 // just the confirmation message. 409 // just the confirmation message.
410 if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) 410 if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
411 { 411 {
412 attron (promptColor); 412 attron(promptColor);
413 mvhline (LINES - 2, 0, ' ', COLS); 413 mvhline(LINES - 2, 0, ' ', COLS);
414 mvprintw (LINES - 2, 0, "Are you sure you want to disconnect? y/n"); 414 mvprintw(LINES - 2, 0, "Are you sure you want to disconnect? y/n");
415 attroff (promptColor); 415 attroff(promptColor);
416 m_needRefresh = true; 416 m_needRefresh = true;
417 return; 417 return;
418 } 418 }
419 419
420 String prompt = getPromptString(); 420 String prompt = getPromptString();
428 for (char &ch : displayString) 428 for (char &ch : displayString)
429 ch = '*'; 429 ch = '*';
430 } 430 }
431 431
432 // Ensure the cursor is within bounds 432 // Ensure the cursor is within bounds
433 m_cursorPosition = clamp (m_cursorPosition, 0, displayString.length()); 433 m_cursorPosition = clamp(m_cursorPosition, 0, displayString.length());
434 434
435 // Ensure that the cursor is always in view, adjust panning if this is not the case 435 // Ensure that the cursor is always in view, adjust panning if this is not the case
436 if (m_cursorPosition > m_inputPanning + displayLength) 436 if (m_cursorPosition > m_inputPanning + displayLength)
437 m_inputPanning = m_cursorPosition - displayLength; // cursor went too far right 437 m_inputPanning = m_cursorPosition - displayLength; // cursor went too far right
438 else if (m_cursorPosition < m_inputPanning) 438 else if (m_cursorPosition < m_inputPanning)
439 m_inputPanning = m_cursorPosition; // cursor went past the pan value to the left 439 m_inputPanning = m_cursorPosition; // cursor went past the pan value to the left
440 440
441 // What part of the string to draw? 441 // What part of the string to draw?
442 int start = m_inputPanning; 442 int start = m_inputPanning;
443 int end = min<int> (displayString.length(), start + displayLength); 443 int end = min<int>(displayString.length(), start + displayLength);
444 assert (m_cursorPosition >= start and m_cursorPosition <= end); 444 assert(m_cursorPosition >= start and m_cursorPosition <= end);
445 445
446 // Render the input string 446 // Render the input string
447 mvhline (LINES - 2, 0, ' ', COLS); 447 mvhline(LINES - 2, 0, ' ', COLS);
448 mvprintw (y, prompt.length() + 1, "%s", displayString.mid (start, end).chars()); 448 mvprintw(y, prompt.length() + 1, "%s", displayString.mid(start, end).chars());
449 449
450 // Render the prompt 450 // Render the prompt
451 attron (promptColor); 451 attron(promptColor);
452 mvprintw (y, 0, "%s", prompt.chars()); 452 mvprintw(y, 0, "%s", prompt.chars());
453 attroff (promptColor); 453 attroff(promptColor);
454 454
455 // Store in memory where the cursor is now (so that we can re-draw it to position the terminal 455 // Store in memory where the cursor is now(so that we can re-draw it to position the terminal
456 // cursor). 456 // cursor).
457 m_cursorCharacter.ch = m_cursorPosition != 0 ? displayString[m_cursorPosition - 1] : '\0'; 457 m_cursorCharacter.ch = m_cursorPosition != 0 ? displayString[m_cursorPosition - 1] : '\0';
458 m_cursorCharacter.x = prompt.length() + (m_cursorPosition - m_inputPanning); 458 m_cursorCharacter.x = prompt.length() + (m_cursorPosition - m_inputPanning);
459 m_needRefresh = true; 459 m_needRefresh = true;
460 m_needInputRender = false; 460 m_needInputRender = false;
462 462
463 // ------------------------------------------------------------------------------------------------- 463 // -------------------------------------------------------------------------------------------------
464 // 464 //
465 void Interface::renderStatusBar() 465 void Interface::renderStatusBar()
466 { 466 {
467 chtype color = getColorPair (WHITE, BLUE); 467 chtype color = getColorPair(WHITE, BLUE);
468 int y = LINES - 1; 468 int y = LINES - 1;
469 attron (color); 469 attron(color);
470 mvhline (y, 0, ' ', COLS); 470 mvhline(y, 0, ' ', COLS);
471 mvprintw (y, 0, "%s", m_statusBarText.chars()); 471 mvprintw(y, 0, "%s", m_statusBarText.chars());
472 attroff (color); 472 attroff(color);
473 m_needRefresh = true; 473 m_needRefresh = true;
474 m_needStatusBarRender = false; 474 m_needStatusBarRender = false;
475 } 475 }
476 476
477 // ------------------------------------------------------------------------------------------------- 477 // -------------------------------------------------------------------------------------------------
486 text = "Disconnected."; 486 text = "Disconnected.";
487 break; 487 break;
488 488
489 case RCON_CONNECTING: 489 case RCON_CONNECTING:
490 case RCON_AUTHENTICATING: 490 case RCON_AUTHENTICATING:
491 text = "Connecting to " + m_session.address().to_string (IPAddress::WITH_PORT) + "..."; 491 text = "Connecting to " + m_session.address().to_string(IPAddress::WITH_PORT) + "...";
492 break; 492 break;
493 493
494 case RCON_CONNECTED: 494 case RCON_CONNECTED:
495 { 495 {
496 String adminText; 496 String adminText;
499 { 499 {
500 adminText = "No other admins"; 500 adminText = "No other admins";
501 } 501 }
502 else 502 else
503 { 503 {
504 adminText.sprintf ("%d other admin%s", m_session.num_admins(), 504 adminText.sprintf("%d other admin%s", m_session.num_admins(),
505 m_session.num_admins() != 1 ? "s" : ""); 505 m_session.num_admins() != 1 ? "s" : "");
506 } 506 }
507 507
508 text.sprintf ("%s | %s | %s", 508 text.sprintf("%s | %s | %s",
509 m_session.address().to_string (IPAddress::WITH_PORT).chars(), 509 m_session.address().to_string(IPAddress::WITH_PORT).chars(),
510 m_session.level().chars(), 510 m_session.level().chars(),
511 adminText.chars()); 511 adminText.chars());
512 } 512 }
513 break; 513 break;
514 } 514 }
515 515
516 if (not text.is_empty()) 516 if (not text.is_empty())
517 text += " | "; 517 text += " | ";
518 518
519 text += "Ctrl+N to connect, Ctrl+Q to "; 519 text += "Ctrl+N to connect, Ctrl+Q to ";
520 text += (m_session.state() == RCON_DISCONNECTED) ? "quit" : "disconnect"; 520 text +=(m_session.state() == RCON_DISCONNECTED) ? "quit" : "disconnect";
521 521
522 if (text != m_statusBarText) 522 if (text != m_statusBarText)
523 { 523 {
524 m_statusBarText = text; 524 m_statusBarText = text;
525 m_needStatusBarRender = true; 525 m_needStatusBarRender = true;
547 return; 547 return;
548 548
549 int y = LINES - 2; 549 int y = LINES - 2;
550 550
551 if (m_cursorCharacter.ch != '\0') 551 if (m_cursorCharacter.ch != '\0')
552 mvprintw (y, m_cursorCharacter.x, "%c", m_cursorCharacter.ch); 552 mvprintw(y, m_cursorCharacter.x, "%c", m_cursorCharacter.ch);
553 else 553 else
554 mvprintw (y, getPromptString().length(), " "); 554 mvprintw(y, getPromptString().length(), " ");
555 } 555 }
556 556
557 // ------------------------------------------------------------------------------------------------- 557 // -------------------------------------------------------------------------------------------------
558 // 558 //
559 int Interface::findPreviousWord() 559 int Interface::findPreviousWord()
560 { 560 {
561 const String& input = getCurrentInput(); 561 const String& input = getCurrentInput();
562 int pos = m_cursorPosition; 562 int pos = m_cursorPosition;
563 563
564 // Move past whitespace 564 // Move past whitespace
565 while (pos > 0 and isspace (input[pos - 1])) 565 while (pos > 0 and isspace(input[pos - 1]))
566 pos--; 566 pos--;
567 567
568 // Move past the word 568 // Move past the word
569 while (pos > 0 and not isspace (input[pos - 1])) 569 while (pos > 0 and not isspace(input[pos - 1]))
570 pos--; 570 pos--;
571 571
572 return pos; 572 return pos;
573 } 573 }
574 574
578 { 578 {
579 const String& input = getCurrentInput(); 579 const String& input = getCurrentInput();
580 int pos = m_cursorPosition; 580 int pos = m_cursorPosition;
581 581
582 // Move past current whitespace 582 // Move past current whitespace
583 while (pos < input.length() and isspace (input[pos])) 583 while (pos < input.length() and isspace(input[pos]))
584 pos++; 584 pos++;
585 585
586 // Move past the word 586 // Move past the word
587 while (input[pos] != '\0' and not isspace (input[pos])) 587 while (input[pos] != '\0' and not isspace(input[pos]))
588 pos++; 588 pos++;
589 589
590 return pos; 590 return pos;
591 } 591 }
592 592
593 // ------------------------------------------------------------------------------------------------- 593 // -------------------------------------------------------------------------------------------------
594 // 594 //
595 void Interface::yank (int a, int b) 595 void Interface::yank(int a, int b)
596 { 596 {
597 if (a >= b) 597 if (a >= b)
598 return; 598 return;
599 599
600 if (m_cursorPosition > a and m_cursorPosition <= b) 600 if (m_cursorPosition > a and m_cursorPosition <= b)
601 m_cursorPosition = a; 601 m_cursorPosition = a;
602 602
603 String& input = getEditableInput(); 603 String& input = getEditableInput();
604 m_pasteBuffer = input.mid (a, b); 604 m_pasteBuffer = input.mid(a, b);
605 input.remove (a, b - a); 605 input.remove(a, b - a);
606 m_needInputRender = true; 606 m_needInputRender = true;
607 } 607 }
608 608
609 // ------------------------------------------------------------------------------------------------- 609 // -------------------------------------------------------------------------------------------------
610 // 610 //
628 { 628 {
629 m_session.disconnect(); 629 m_session.disconnect();
630 m_disconnectCallback(true); 630 m_disconnectCallback(true);
631 } 631 }
632 else if (ch == 'n' or ch == 'N') 632 else if (ch == 'n' or ch == 'N')
633 setInputState (INPUTSTATE_NORMAL); 633 setInputState(INPUTSTATE_NORMAL);
634 634
635 return; 635 return;
636 } 636 }
637 637
638 if (ch >= 0x20 and ch <= 0x7E) 638 if (ch >= 0x20 and ch <= 0x7E)
639 { 639 {
640 getEditableInput().insert (m_cursorPosition++, char (ch)); 640 getEditableInput().insert(m_cursorPosition++, char(ch));
641 m_needInputRender = true; 641 m_needInputRender = true;
642 } 642 }
643 else switch (ch) 643 else switch (ch)
644 { 644 {
645 case 'Q' - 'A' + 1: // ^Q 645 case 'Q' - 'A' + 1: // ^Q
647 { 647 {
648 case INPUTSTATE_CONFIRM_DISCONNECTION: 648 case INPUTSTATE_CONFIRM_DISCONNECTION:
649 break; 649 break;
650 650
651 case INPUTSTATE_NORMAL: 651 case INPUTSTATE_NORMAL:
652 safeDisconnect ([&](bool hadsession) 652 safeDisconnect([&](bool hadsession)
653 { 653 {
654 if (hadsession) 654 if (hadsession)
655 { 655 {
656 setInputState (INPUTSTATE_NORMAL); 656 setInputState(INPUTSTATE_NORMAL);
657 } 657 }
658 else 658 else
659 { 659 {
660 endwin(); 660 endwin();
661 throw Exitception(); 661 throw Exitception();
662 } 662 }
663 }); 663 });
664 break; 664 break;
665 665
666 case INPUTSTATE_PASSWORD: 666 case INPUTSTATE_PASSWORD:
667 setInputState (INPUTSTATE_ADDRESS); 667 setInputState(INPUTSTATE_ADDRESS);
668 break; 668 break;
669 669
670 case INPUTSTATE_ADDRESS: 670 case INPUTSTATE_ADDRESS:
671 setInputState (INPUTSTATE_NORMAL); 671 setInputState(INPUTSTATE_NORMAL);
672 } 672 }
673 break; 673 break;
674 674
675 case KEY_LEFT: 675 case KEY_LEFT:
676 case 'B' - 'A' + 1: // readline ^B 676 case 'B' - 'A' + 1: // readline ^B
690 } 690 }
691 break; 691 break;
692 692
693 case KEY_DOWN: 693 case KEY_DOWN:
694 case KEY_UP: 694 case KEY_UP:
695 moveInputCursor (ch == KEY_DOWN ? -1 : 1); 695 moveInputCursor(ch == KEY_DOWN ? -1 : 1);
696 break; 696 break;
697 697
698 case KEY_HOME: 698 case KEY_HOME:
699 case 'A' - 'A' + 1: // readline ^A 699 case 'A' - 'A' + 1: // readline ^A
700 if (m_cursorPosition != 0) 700 if (m_cursorPosition != 0)
715 715
716 case KEY_BACKSPACE: 716 case KEY_BACKSPACE:
717 case '\b': 717 case '\b':
718 if (m_cursorPosition > 0) 718 if (m_cursorPosition > 0)
719 { 719 {
720 getEditableInput().remove_at (--m_cursorPosition); 720 getEditableInput().remove_at(--m_cursorPosition);
721 m_needInputRender = true; 721 m_needInputRender = true;
722 } 722 }
723 break; 723 break;
724 724
725 case KEY_DC: 725 case KEY_DC:
726 case 'D' - 'A' + 1: // readline ^D 726 case 'D' - 'A' + 1: // readline ^D
727 if (m_cursorPosition < getCurrentInput().length()) 727 if (m_cursorPosition < getCurrentInput().length())
728 { 728 {
729 getEditableInput().remove_at (m_cursorPosition); 729 getEditableInput().remove_at(m_cursorPosition);
730 m_needInputRender = true; 730 m_needInputRender = true;
731 } 731 }
732 break; 732 break;
733 733
734 case KEY_PPAGE: 734 case KEY_PPAGE:
735 m_outputScroll += min (PAGE_SIZE, LINES / 2); 735 m_outputScroll += min(PAGE_SIZE, LINES / 2);
736 m_needOutputRender = true; 736 m_needOutputRender = true;
737 break; 737 break;
738 738
739 case KEY_NPAGE: 739 case KEY_NPAGE:
740 m_outputScroll -= min (PAGE_SIZE, LINES / 2); 740 m_outputScroll -= min(PAGE_SIZE, LINES / 2);
741 m_needOutputRender = true; 741 m_needOutputRender = true;
742 break; 742 break;
743 743
744 case 'U' - 'A' + 1: // readline ^U - delete from start to cursor 744 case 'U' - 'A' + 1: // readline ^U - delete from start to cursor
745 if (m_cursorPosition > 0) 745 if (m_cursorPosition > 0)
746 { 746 {
747 yank (0, m_cursorPosition); 747 yank(0, m_cursorPosition);
748 m_cursorPosition = 0; 748 m_cursorPosition = 0;
749 } 749 }
750 break; 750 break;
751 751
752 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end 752 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end
753 yank (m_cursorPosition, getEditableInput().length()); 753 yank(m_cursorPosition, getEditableInput().length());
754 break; 754 break;
755 755
756 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current 756 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current
757 yank (findPreviousWord(), m_cursorPosition); 757 yank(findPreviousWord(), m_cursorPosition);
758 break; 758 break;
759 759
760 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text 760 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text
761 if (not m_pasteBuffer.is_empty()) 761 if (not m_pasteBuffer.is_empty())
762 { 762 {
763 getEditableInput().insert (m_cursorPosition, m_pasteBuffer); 763 getEditableInput().insert(m_cursorPosition, m_pasteBuffer);
764 m_cursorPosition += m_pasteBuffer.length(); 764 m_cursorPosition += m_pasteBuffer.length();
765 m_needInputRender = true; 765 m_needInputRender = true;
766 } 766 }
767 break; 767 break;
768 768
769 case '\t': 769 case '\t':
770 { 770 {
771 int space = getCurrentInput().find (" "); 771 int space = getCurrentInput().find(" ");
772 772
773 if (m_inputState == INPUTSTATE_NORMAL 773 if (m_inputState == INPUTSTATE_NORMAL
774 and m_cursorPosition > 0 774 and m_cursorPosition > 0
775 and (space == -1 or space >= m_cursorPosition)) 775 and(space == -1 or space >= m_cursorPosition))
776 { 776 {
777 String start = getCurrentInput().mid (0, m_cursorPosition); 777 String start = getCurrentInput().mid(0, m_cursorPosition);
778 m_session.request_tab_complete (start); 778 m_session.request_tab_complete(start);
779 } 779 }
780 } 780 }
781 break; 781 break;
782 782
783 case '\n': 783 case '\n':
789 break; // handled above 789 break; // handled above
790 790
791 case INPUTSTATE_ADDRESS: 791 case INPUTSTATE_ADDRESS:
792 try 792 try
793 { 793 {
794 m_remoteAddress = IPAddress::from_string (getCurrentInput()); 794 m_remoteAddress = IPAddress::from_string(getCurrentInput());
795 } 795 }
796 catch (std::exception& e) 796 catch (std::exception& e)
797 { 797 {
798 print ("%s\n", e.what()); 798 print("%s\n", e.what());
799 return; 799 return;
800 } 800 }
801 801
802 if (m_remoteAddress.port == 0) 802 if (m_remoteAddress.port == 0)
803 m_remoteAddress.port = 10666; 803 m_remoteAddress.port = 10666;
804 804
805 setInputState (INPUTSTATE_PASSWORD); 805 setInputState(INPUTSTATE_PASSWORD);
806 break; 806 break;
807 807
808 case INPUTSTATE_PASSWORD: 808 case INPUTSTATE_PASSWORD:
809 if (m_inputState == INPUTSTATE_PASSWORD and not getCurrentInput().is_empty()) 809 if (m_inputState == INPUTSTATE_PASSWORD and not getCurrentInput().is_empty())
810 { 810 {
811 m_session.disconnect(); 811 m_session.disconnect();
812 m_session.set_password (getCurrentInput()); 812 m_session.set_password(getCurrentInput());
813 m_session.connect (m_remoteAddress); 813 m_session.connect(m_remoteAddress);
814 setInputState (INPUTSTATE_NORMAL); 814 setInputState(INPUTSTATE_NORMAL);
815 } 815 }
816 break; 816 break;
817 817
818 case INPUTSTATE_NORMAL: 818 case INPUTSTATE_NORMAL:
819 if (getCurrentInput()[0] == '/') 819 if (getCurrentInput()[0] == '/')
820 { 820 {
821 handleCommand(getCurrentInput()); 821 handleCommand(getCurrentInput());
822 flushInput(); 822 flushInput();
823 } 823 }
824 else if (m_session.send_command (getCurrentInput())) 824 else if (m_session.send_command(getCurrentInput()))
825 { 825 {
826 flushInput(); 826 flushInput();
827 } 827 }
828 break; 828 break;
829 } 829 }
830 break; 830 break;
831 831
832 case 'N' - 'A' + 1: // ^N 832 case 'N' - 'A' + 1: // ^N
833 if (m_inputState == INPUTSTATE_NORMAL) 833 if (m_inputState == INPUTSTATE_NORMAL)
834 safeDisconnect ([&](bool){setInputState (INPUTSTATE_ADDRESS);}); 834 safeDisconnect([&](bool){setInputState(INPUTSTATE_ADDRESS);});
835 break; 835 break;
836 836
837 case '\x1b': // Escape 837 case '\x1b': // Escape
838 // We may have an alt key coming 838 // We may have an alt key coming
839 ch = ::getch(); 839 ch = ::getch();
857 break; 857 break;
858 858
859 case 'd': 859 case 'd':
860 case 'D': 860 case 'D':
861 // readline alt-d - delete from here till next word boundary 861 // readline alt-d - delete from here till next word boundary
862 yank (m_cursorPosition, findNextWord()); 862 yank(m_cursorPosition, findNextWord());
863 break; 863 break;
864 864
865 case KEY_BACKSPACE: // alt+backspace, remove previous word 865 case KEY_BACKSPACE: // alt+backspace, remove previous word
866 case '\b': 866 case '\b':
867 yank (findPreviousWord(), m_cursorPosition); 867 yank(findPreviousWord(), m_cursorPosition);
868 break; 868 break;
869 } 869 }
870 } 870 }
871 else 871 else
872 { 872 {
873 // No alt-key, handle pure escape 873 // No alt-key, handle pure escape
874 if (m_inputState == INPUTSTATE_PASSWORD) 874 if (m_inputState == INPUTSTATE_PASSWORD)
875 setInputState (INPUTSTATE_ADDRESS); 875 setInputState(INPUTSTATE_ADDRESS);
876 else if (m_inputState == INPUTSTATE_ADDRESS) 876 else if (m_inputState == INPUTSTATE_ADDRESS)
877 setInputState (INPUTSTATE_NORMAL); 877 setInputState(INPUTSTATE_NORMAL);
878 } 878 }
879 break; 879 break;
880 } 880 }
881 881
882 render(); 882 render();
899 } 899 }
900 } 900 }
901 901
902 // ------------------------------------------------------------------------------------------------- 902 // -------------------------------------------------------------------------------------------------
903 // 903 //
904 void Interface::vprint (const char* fmtstr, va_list args) 904 void Interface::vprint(const char* fmtstr, va_list args)
905 { 905 {
906 String message; 906 String message;
907 message.vsprintf (fmtstr, args); 907 message.vsprintf(fmtstr, args);
908 printToConsole (message); 908 printToConsole(message);
909 } 909 }
910 910
911 // ------------------------------------------------------------------------------------------------- 911 // -------------------------------------------------------------------------------------------------
912 // 912 //
913 void __cdecl Interface::printText (const char* fmtstr, ...) 913 void __cdecl Interface::printText(const char* fmtstr, ...)
914 { 914 {
915 va_list args; 915 va_list args;
916 va_start (args, fmtstr); 916 va_start(args, fmtstr);
917 vprint (fmtstr, args); 917 vprint(fmtstr, args);
918 va_end (args); 918 va_end(args);
919 } 919 }
920 920
921 // ------------------------------------------------------------------------------------------------- 921 // -------------------------------------------------------------------------------------------------
922 // 922 //
923 void __cdecl Interface::print (const char* fmtstr, ...) 923 void __cdecl Interface::print(const char* fmtstr, ...)
924 { 924 {
925 va_list args; 925 va_list args;
926 va_start (args, fmtstr); 926 va_start(args, fmtstr);
927 printToConsole (TEXTCOLOR_BrightBlue); 927 printToConsole(TEXTCOLOR_BrightBlue);
928 vprint (fmtstr, args); 928 vprint(fmtstr, args);
929 va_end (args); 929 va_end(args);
930 } 930 }
931 931
932 // ------------------------------------------------------------------------------------------------- 932 // -------------------------------------------------------------------------------------------------
933 // 933 //
934 void __cdecl Interface::printWarning (const char* fmtstr, ...) 934 void __cdecl Interface::printWarning(const char* fmtstr, ...)
935 { 935 {
936 va_list args; 936 va_list args;
937 va_start (args, fmtstr); 937 va_start(args, fmtstr);
938 printToConsole (TEXTCOLOR_BrightYellow "-!- "); 938 printToConsole(TEXTCOLOR_BrightYellow "-!- ");
939 vprint (fmtstr, args); 939 vprint(fmtstr, args);
940 va_end (args); 940 va_end(args);
941 } 941 }
942 942
943 // ------------------------------------------------------------------------------------------------- 943 // -------------------------------------------------------------------------------------------------
944 // 944 //
945 void __cdecl Interface::printError (const char* fmtstr, ...) 945 void __cdecl Interface::printError(const char* fmtstr, ...)
946 { 946 {
947 va_list args; 947 va_list args;
948 va_start (args, fmtstr); 948 va_start(args, fmtstr);
949 printToConsole (TEXTCOLOR_BrightRed "!!! "); 949 printToConsole(TEXTCOLOR_BrightRed "!!! ");
950 vprint (fmtstr, args); 950 vprint(fmtstr, args);
951 va_end (args); 951 va_end(args);
952 } 952 }
953 953
954 // ------------------------------------------------------------------------------------------------- 954 // -------------------------------------------------------------------------------------------------
955 // 955 //
956 void Interface::printToConsole (String message) 956 void Interface::printToConsole(String message)
957 { 957 {
958 // Zandronum sometimes sends color codes as "\\c" and sometimes as "\x1C". 958 // Zandronum sometimes sends color codes as "\\c" and sometimes as "\x1C".
959 // Let's correct that on our end and hope this won't cause conflicts. 959 // Let's correct that on our end and hope this won't cause conflicts.
960 message.replace ("\\c", "\x1C"); 960 message.replace("\\c", "\x1C");
961 961
962 for (char ch : message) 962 for (char ch : message)
963 { 963 {
964 if (ch == '\n') 964 if (ch == '\n')
965 { 965 {
969 } 969 }
970 970
971 if (m_outputLines.last().length() == 0) 971 if (m_outputLines.last().length() == 0)
972 { 972 {
973 time_t now; 973 time_t now;
974 time (&now); 974 time(&now);
975 char timestamp[32]; 975 char timestamp[32];
976 strftime (timestamp, sizeof timestamp, "[%H:%M:%S] ", localtime (&now)); 976 strftime(timestamp, sizeof timestamp, "[%H:%M:%S] ", localtime(&now));
977 977
978 for (char ch : String(timestamp)) 978 for (char ch : String(timestamp))
979 m_outputLines.last().add_char (ch); 979 m_outputLines.last().add_char(ch);
980 } 980 }
981 981
982 // Remove some lines if there's too many of them. 20,000 should be enough, I hope. 982 // Remove some lines if there's too many of them. 20,000 should be enough, I hope.
983 while (m_outputLines.size() > 20000) 983 while (m_outputLines.size() > 20000)
984 m_outputLines.remove_at(0); 984 m_outputLines.remove_at(0);
985 985
986 m_outputLines.last().add_char (ch); 986 m_outputLines.last().add_char(ch);
987 } 987 }
988 988
989 m_needOutputRender = true; 989 m_needOutputRender = true;
990 } 990 }
991 991
992 // ------------------------------------------------------------------------------------------------- 992 // -------------------------------------------------------------------------------------------------
993 // 993 //
994 void Interface::connect (String address, String password) 994 void Interface::connect(String address, String password)
995 { 995 {
996 try 996 try
997 { 997 {
998 m_remoteAddress = IPAddress::from_string (address); 998 m_remoteAddress = IPAddress::from_string(address);
999 } 999 }
1000 catch (std::exception& e) 1000 catch (std::exception& e)
1001 { 1001 {
1002 print ("%s\n", e.what()); 1002 print("%s\n", e.what());
1003 return; 1003 return;
1004 } 1004 }
1005 1005
1006 if (m_remoteAddress.port == 0) 1006 if (m_remoteAddress.port == 0)
1007 m_remoteAddress.port = 10666; 1007 m_remoteAddress.port = 10666;
1008 1008
1009 m_session.disconnect(); 1009 m_session.disconnect();
1010 m_session.set_password (password); 1010 m_session.set_password(password);
1011 m_session.connect (m_remoteAddress); 1011 m_session.connect(m_remoteAddress);
1012 } 1012 }
1013 1013
1014 // ------------------------------------------------------------------------------------------------- 1014 // -------------------------------------------------------------------------------------------------
1015 // 1015 //
1016 void Interface::setPlayerNames (const StringList& names) 1016 void Interface::setPlayerNames(const StringList& names)
1017 { 1017 {
1018 m_playerNames.clear(); 1018 m_playerNames.clear();
1019 1019
1020 for (const String& name : names) 1020 for (const String& name : names)
1021 { 1021 {
1022 ColoredLine coloredname; 1022 ColoredLine coloredname;
1023 coloredname.add_string (name); 1023 coloredname.add_string(name);
1024 coloredname.finalize(); 1024 coloredname.finalize();
1025 m_playerNames.append (coloredname); 1025 m_playerNames.append(coloredname);
1026 } 1026 }
1027 1027
1028 m_needNicklistRender = true; 1028 m_needNicklistRender = true;
1029 } 1029 }
1030 1030
1031 // ------------------------------------------------------------------------------------------------- 1031 // -------------------------------------------------------------------------------------------------
1032 // 1032 //
1033 void Interface::tabComplete (const String& part, String complete) 1033 void Interface::tabComplete(const String& part, String complete)
1034 { 1034 {
1035 String& input = getEditableInput(); 1035 String& input = getEditableInput();
1036 1036
1037 if (input.starts_with (part)) 1037 if (input.starts_with(part))
1038 { 1038 {
1039 if (input[part.length()] != ' ') 1039 if (input[part.length()] != ' ')
1040 complete += ' '; 1040 complete += ' ';
1041 1041
1042 input.replace (0, part.length(), complete); 1042 input.replace(0, part.length(), complete);
1043 m_cursorPosition = complete.length(); 1043 m_cursorPosition = complete.length();
1044 m_needInputRender = true; 1044 m_needInputRender = true;
1045 } 1045 }
1046 } 1046 }
1047 1047
1109 1109
1110 // ------------------------------------------------------------------------------------------------- 1110 // -------------------------------------------------------------------------------------------------
1111 // 1111 //
1112 void Interface::resetTitle() 1112 void Interface::resetTitle()
1113 { 1113 {
1114 m_title.sprintf ("%s %s (%s)", application_name(), full_version_string(), changeset_date_string()); 1114 m_title.sprintf("%s %s (%s)", application_name(), full_version_string(), changeset_date_string());
1115 } 1115 }
1116 1116
1117 // ------------------------------------------------------------------------------------------------- 1117 // -------------------------------------------------------------------------------------------------
1118 // 1118 //
1119 void Interface::flushInput() 1119 void Interface::flushInput()
1120 { 1120 {
1121 m_inputHistory.insert (0, ""); 1121 m_inputHistory.insert(0, "");
1122 m_inputCursor = 0; 1122 m_inputCursor = 0;
1123 m_needInputRender = true; 1123 m_needInputRender = true;
1124 } 1124 }
1125 1125
1126 END_ZFC_NAMESPACE 1126 END_ZFC_NAMESPACE

mercurial