29 */ |
29 */ |
30 |
30 |
31 #include <string.h> |
31 #include <string.h> |
32 #include "interface.h" |
32 #include "interface.h" |
33 #include "network/rconsession.h" |
33 #include "network/rconsession.h" |
|
34 #include "network/ipaddress.h" |
34 |
35 |
35 enum { PAGE_SIZE = 10 }; |
36 enum { PAGE_SIZE = 10 }; |
|
37 |
|
38 enum InputState |
|
39 { |
|
40 INPUTSTATE_NORMAL, |
|
41 INPUTSTATE_ADDRESS, |
|
42 INPUTSTATE_PASSWORD, |
|
43 INPUTSTATE_CONFIRM_DISCONNECTION, |
|
44 }; |
36 |
45 |
37 static String g_input; |
46 static String g_input; |
38 static int g_cursor = 0; |
47 static int g_cursor = 0; |
39 static int g_pan = 0; |
48 static int g_pan = 0; |
40 static bool g_needRefresh = false; |
49 static bool g_needRefresh = false; |
44 static String g_statusBarText; |
53 static String g_statusBarText; |
45 static struct { char ch; int x; } g_cursorChar; |
54 static struct { char ch; int x; } g_cursorChar; |
46 static Vector<String> g_output = {""}; |
55 static Vector<String> g_output = {""}; |
47 static int g_outputScroll = 0; |
56 static int g_outputScroll = 0; |
48 static String g_title; |
57 static String g_title; |
|
58 static InputState g_inputState = INPUTSTATE_NORMAL; |
|
59 static IPAddress g_address; |
49 |
60 |
50 // ------------------------------------------------------------------------------------------------- |
61 // ------------------------------------------------------------------------------------------------- |
51 // |
62 // |
52 static FUNCTION |
63 static FUNCTION |
53 interface_color_pair (Color fg, Color bg) -> int |
64 interface_color_pair (Color fg, Color bg) -> int |
54 { |
65 { |
55 return COLOR_PAIR ((int (fg) * NUM_COLORS) + int (bg)); |
66 return COLOR_PAIR ((int (fg) * NUM_COLORS) + int (bg)); |
|
67 } |
|
68 |
|
69 // ------------------------------------------------------------------------------------------------- |
|
70 // |
|
71 static FUNCTION |
|
72 interface_prompt_string() -> String |
|
73 { |
|
74 String prompt; |
|
75 |
|
76 switch (g_inputState) |
|
77 { |
|
78 case INPUTSTATE_NORMAL: prompt = ">"; break; |
|
79 case INPUTSTATE_ADDRESS: prompt = "address:"; break; |
|
80 case INPUTSTATE_PASSWORD: prompt = "password:"; break; |
|
81 case INPUTSTATE_CONFIRM_DISCONNECTION: break; |
|
82 } |
|
83 |
|
84 return prompt; |
56 } |
85 } |
57 |
86 |
58 // ------------------------------------------------------------------------------------------------- |
87 // ------------------------------------------------------------------------------------------------- |
59 // |
88 // |
60 FUNCTION |
89 FUNCTION |
151 // ------------------------------------------------------------------------------------------------- |
180 // ------------------------------------------------------------------------------------------------- |
152 // |
181 // |
153 static FUNCTION |
182 static FUNCTION |
154 interface_render_input() -> void |
183 interface_render_input() -> void |
155 { |
184 { |
156 static char prompt[] = ">"; |
185 int pair = interface_color_pair (WHITE, BLUE); |
157 int displaylength = COLS - strlen (prompt) - 2; |
186 |
|
187 if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
|
188 { |
|
189 attron (pair); |
|
190 mvhline (LINES - 2, 0, ' ', COLS); |
|
191 mvprintw (LINES - 2, 0, "Are you sure you want to disconnect? y/n"); |
|
192 attroff (pair); |
|
193 return; |
|
194 } |
|
195 |
|
196 String prompt = interface_prompt_string(); |
|
197 int displaylength = COLS - prompt.length() - 2; |
158 int y = LINES - 2; |
198 int y = LINES - 2; |
159 |
199 |
160 // Ensure the cursor is within bounds |
200 // Ensure the cursor is within bounds |
161 g_cursor = clamp (g_cursor, 0, g_input.length()); |
201 g_cursor = clamp (g_cursor, 0, g_input.length()); |
162 |
202 |
168 |
208 |
169 int start = g_pan; |
209 int start = g_pan; |
170 int end = min<int> (g_input.length(), start + displaylength); |
210 int end = min<int> (g_input.length(), start + displaylength); |
171 assert (g_cursor >= start and g_cursor <= end); |
211 assert (g_cursor >= start and g_cursor <= end); |
172 |
212 |
173 // Clear, but only as much as is necessary. I want to avoid clearing the entire line to save |
|
174 // bandwidth over SSH connections. Perhaps needlessly? I'm paranoid. |
|
175 int renderLength = strlen (prompt) + 1 + g_input.length(); |
|
176 static int lastRenderLength = 0; |
|
177 |
|
178 if (lastRenderLength > renderLength) |
|
179 mvhline (y, renderLength, ' ', lastRenderLength - renderLength); |
|
180 |
|
181 lastRenderLength = renderLength; |
|
182 |
|
183 // Render the input line, with the part of the input string that's before the cursor written |
213 // Render the input line, with the part of the input string that's before the cursor written |
184 // AFTER the part that comes afterwards. This is to ensure that the cursor is placed at the |
214 // AFTER the part that comes afterwards. This is to ensure that the cursor is placed at the |
185 // position where our cursor is. It looks nice like that. :) |
215 // position where our cursor is. It looks nice like that. :) |
186 |
216 |
187 int pair = interface_color_pair (WHITE, BLUE); |
217 mvhline (LINES - 2, 0, ' ', COLS); |
|
218 mvprintw (y, prompt.length() + 1, "%s", g_input.chars()); |
188 attron (pair); |
219 attron (pair); |
189 mvprintw (y, 0, "%s", prompt); |
220 mvprintw (y, 0, "%s", prompt.chars()); |
190 attroff (pair); |
221 attroff (pair); |
191 mvprintw (y, strlen (prompt) + 1, "%s", g_input.chars()); |
|
192 g_cursorChar.ch = g_cursor != 0 ? g_input[g_cursor - 1] : '\0'; |
222 g_cursorChar.ch = g_cursor != 0 ? g_input[g_cursor - 1] : '\0'; |
193 g_cursorChar.x = strlen (prompt) + (g_cursor - g_pan); |
223 g_cursorChar.x = prompt.length() + (g_cursor - g_pan); |
194 g_needRefresh = true; |
224 g_needRefresh = true; |
195 g_needInputRender = false; |
225 g_needInputRender = false; |
196 } |
226 } |
197 |
227 |
198 // ------------------------------------------------------------------------------------------------- |
228 // ------------------------------------------------------------------------------------------------- |
235 int y = LINES - 2; |
265 int y = LINES - 2; |
236 |
266 |
237 if (g_cursorChar.ch != '\0') |
267 if (g_cursorChar.ch != '\0') |
238 mvprintw (y, g_cursorChar.x, "%c", g_cursorChar.ch); |
268 mvprintw (y, g_cursorChar.x, "%c", g_cursorChar.ch); |
239 else |
269 else |
240 mvprintw (y, 1, " "); |
270 mvprintw (y, interface_prompt_string().length(), " "); |
241 } |
271 } |
242 |
272 |
243 // ------------------------------------------------------------------------------------------------- |
273 // ------------------------------------------------------------------------------------------------- |
244 // |
274 // |
245 FUNCTION |
275 FUNCTION |
246 Interface::handle_input() -> void |
276 Interface::handle_input() -> void |
247 { |
277 { |
248 int ch = ::getch(); |
278 int ch = ::getch(); |
249 set_statusbar_text (String::from_number (ch)); |
279 set_statusbar_text (String::from_number (ch)); |
|
280 |
|
281 if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
|
282 { |
|
283 if (ch == 'y' or ch == 'Y') |
|
284 g_inputState = INPUTSTATE_ADDRESS; |
|
285 else if (ch == 'n' or ch == 'N') |
|
286 g_inputState = INPUTSTATE_NORMAL; |
|
287 return; |
|
288 } |
250 |
289 |
251 if (ch >= 0x20 and ch <= 0x7E) |
290 if (ch >= 0x20 and ch <= 0x7E) |
252 { |
291 { |
253 g_input.insert (g_cursor++, char (ch)); |
292 g_input.insert (g_cursor++, char (ch)); |
254 g_needInputRender = true; |
293 g_needInputRender = true; |
318 g_needOutputRender = true; |
357 g_needOutputRender = true; |
319 break; |
358 break; |
320 |
359 |
321 case '\n': |
360 case '\n': |
322 case KEY_ENTER: |
361 case KEY_ENTER: |
323 if (RCONSession::get_session()->send_command (g_input)) |
362 switch (g_inputState) |
324 { |
363 { |
|
364 case INPUTSTATE_CONFIRM_DISCONNECTION: |
|
365 break; // handled above |
|
366 |
|
367 case INPUTSTATE_ADDRESS: |
|
368 try |
|
369 { |
|
370 g_address = IPAddress::from_string (g_input); |
|
371 } |
|
372 catch (std::exception& e) |
|
373 { |
|
374 print (e.what()); |
|
375 return; |
|
376 } |
|
377 |
|
378 if (g_address.port == 0) |
|
379 g_address.port = 10666; |
|
380 |
325 g_input.clear(); |
381 g_input.clear(); |
|
382 g_inputState = INPUTSTATE_PASSWORD; |
|
383 g_needInputRender = true; |
|
384 |
|
385 case INPUTSTATE_PASSWORD: |
|
386 if (g_inputState == INPUTSTATE_PASSWORD and not g_input.is_empty()) |
|
387 { |
|
388 RCONSession* session = RCONSession::new_session(); |
|
389 session->set_password (g_input); |
|
390 session->connect (g_address); |
|
391 g_input.clear(); |
|
392 g_inputState = INPUTSTATE_NORMAL; |
|
393 g_needInputRender = true; |
|
394 } |
|
395 break; |
|
396 |
|
397 case INPUTSTATE_NORMAL: |
|
398 if (RCONSession::get_session() != nullptr |
|
399 and RCONSession::get_session()->send_command (g_input)) |
|
400 { |
|
401 g_input.clear(); |
|
402 g_needInputRender = true; |
|
403 } |
|
404 break; |
|
405 } |
|
406 break; |
|
407 |
|
408 case 'N' - 'A' + 1: // ^N |
|
409 if (g_inputState == INPUTSTATE_NORMAL) |
|
410 { |
|
411 if (RCONSession::get_session() != nullptr |
|
412 and RCONSession::get_session()->state() != RCON_DISCONNECTED) |
|
413 { |
|
414 g_inputState = INPUTSTATE_CONFIRM_DISCONNECTION; |
|
415 } |
|
416 else |
|
417 { |
|
418 g_inputState = INPUTSTATE_ADDRESS; |
|
419 } |
|
420 |
326 g_needInputRender = true; |
421 g_needInputRender = true; |
327 } |
422 } |
328 break; |
423 break; |
329 } |
424 } |
330 } |
425 } |