OSDN Git Service

[VM][OSD] Add upstream devices/OSD updates.
[csp-qt/common_source_project-fm7.git] / source / src / win32 / osd_console.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2015.11.26-
6
7         [ win32 console ]
8 */
9
10 #include "osd.h"
11 #include "../res/resource.h"
12
13 void OSD::initialize_console()
14 {
15         console_count = 0;
16         use_telnet = config.use_telnet;
17         telnet_closed = true;
18         svr_socket = cli_socket = INVALID_SOCKET;
19 }
20
21 void OSD::release_console()
22 {
23         close_console();
24 }
25
26 BOOL WINAPI ctrl_c_handler(DWORD type)
27 {
28         return TRUE;
29 }
30
31 void OSD::open_console(int width, int height, const _TCHAR* title)
32 {
33         int console_width = (width > 0) ? width : 120;
34         int console_height = (height > 0) ? height : 30;
35         int buffer_height = 9001;
36         
37         if(console_count++ == 0) {
38                 if(use_telnet) {
39                         open_telnet(title);
40                         return;
41                 }
42                 #define SET_RECT(rect, l, t, r, b) { \
43                         rect.Left = l; \
44                         rect.Top = t; \
45                         rect.Right = r; \
46                         rect.Bottom = b; \
47                 }
48                 CONSOLE_SCREEN_BUFFER_INFO csbi;
49                 SMALL_RECT rect;
50                 COORD coord;
51                 
52                 AllocConsole();
53                 SetConsoleTitle(title);
54                 SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
55                 RemoveMenu(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_BYCOMMAND);
56                 
57                 hStdIn = GetStdHandle(STD_INPUT_HANDLE);
58                 hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
59                 GetConsoleScreenBufferInfo(hStdOut, &csbi);
60                 
61                 // window can't be bigger than buffer,
62                 // buffer can't be smaller than window,
63                 // so make a tiny window,
64                 // set the required buffer,
65                 // then set the required window
66                 int min_width  = min(csbi.srWindow.Right - csbi.srWindow.Left + 1, console_width);
67                 int min_height = min(csbi.srWindow.Bottom - csbi.srWindow.Top + 1, console_height);
68                 
69                 SET_RECT(rect, 0, csbi.srWindow.Top, min_width - 1, csbi.srWindow.Top + min_height - 1);
70                 SetConsoleWindowInfo(hStdOut, TRUE, &rect);
71                 
72                 coord.X = console_width;
73                 coord.Y = buffer_height;
74                 SetConsoleScreenBufferSize(hStdOut, coord);
75                 
76                 SET_RECT(rect, 0, 0, console_width - 1, console_height - 1);
77                 if(!SetConsoleWindowInfo(hStdOut, TRUE, &rect)) {
78                         SetWindowPos(GetConsoleWindow(), NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
79                         SetConsoleWindowInfo(hStdOut, TRUE, &rect);
80                 }
81                 SetConsoleTextAttribute(hStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
82         }
83 }
84
85 void OSD::close_console()
86 {
87         if(console_count > 0 && --console_count == 0) {
88                 if(use_telnet) {
89                         close_telnet();
90                         return;
91                 }
92                 SetConsoleCtrlHandler(ctrl_c_handler, FALSE);
93                 FreeConsole();
94         }
95 }
96
97 unsigned int OSD::get_console_code_page()
98 {
99         return GetConsoleCP();
100 }
101
102 void OSD::set_console_text_attribute(unsigned short attr)
103 {
104         unsigned short new_attr = 0;
105         
106         if(use_telnet) {
107                 char buffer[32];
108                 
109                 if(attr & OSD_CONSOLE_BLUE     ) new_attr |= 4;
110                 if(attr & OSD_CONSOLE_GREEN    ) new_attr |= 2;
111                 if(attr & OSD_CONSOLE_RED      ) new_attr |= 1;
112                 if(attr & OSD_CONSOLE_INTENSITY) new_attr |= 8;
113                 
114                 sprintf_s(buffer, 32, "\033[%dm\033[3%dm", (new_attr >> 3) & 1, (new_attr & 7));
115                 send_telnet(buffer);
116                 return;
117         }
118         if(attr & OSD_CONSOLE_BLUE     ) new_attr |= FOREGROUND_BLUE;
119         if(attr & OSD_CONSOLE_GREEN    ) new_attr |= FOREGROUND_GREEN;
120         if(attr & OSD_CONSOLE_RED      ) new_attr |= FOREGROUND_RED;
121         if(attr & OSD_CONSOLE_INTENSITY) new_attr |= FOREGROUND_INTENSITY;
122         
123         SetConsoleTextAttribute(hStdOut, attr);
124 }
125
126 void OSD::write_console(const _TCHAR* buffer, unsigned int length)
127 {
128         if(use_telnet) {
129                 send_telnet(tchar_to_char(buffer));
130                 return;
131         }
132         DWORD dwWritten;
133         WriteConsole(hStdOut, buffer, length, &dwWritten, NULL);
134 }
135
136 int OSD::read_console_input(_TCHAR* buffer, unsigned int length)
137 {
138         if(use_telnet) {
139                 char temp[256];
140                 int len = 0;
141                 if(cli_socket != INVALID_SOCKET && (len = recv(cli_socket, temp, length, 0)) > 0) {
142                         temp[len] = '\0';
143                         const _TCHAR *temp_t = char_to_tchar(temp);
144                         for(int i = 0; i < len; i++) {
145                                 buffer[i] = temp_t[i];
146                         }
147                         return len;
148                 }
149                 if(len == 0) {
150                         telnet_closed = true;
151                 }
152                 return 0;
153         }
154         INPUT_RECORD ir[16];
155         DWORD dwRead;
156         unsigned int count = 0;
157         
158         if(ReadConsoleInput(hStdIn, ir, min(16, length), &dwRead)) {
159                 for(unsigned int i = 0; i < dwRead; i++) {
160                         if((ir[i].EventType & KEY_EVENT) && ir[i].Event.KeyEvent.bKeyDown) {
161 #ifdef _UNICODE
162                                 if(ir[i].Event.KeyEvent.uChar.UnicodeChar) {
163                                         if(count < length) {
164                                                 buffer[count++] = ir[i].Event.KeyEvent.uChar.UnicodeChar;
165                                         }
166 #else
167                                 if(ir[i].Event.KeyEvent.uChar.AsciiChar) {
168                                         if(count < length) {
169                                                 buffer[count++] = ir[i].Event.KeyEvent.uChar.AsciiChar;
170                                         }
171 #endif
172                                 } else if(ir[i].Event.KeyEvent.wVirtualKeyCode >= 0x25 && ir[i].Event.KeyEvent.wVirtualKeyCode <= 0x28) {
173                                         static const _TCHAR cursor[] = {_T('D'), _T('A'), _T('C'), _T('B')}; // left, up, right, down
174                                         if(count + 2 < length) {
175                                                 buffer[count++] = 0x1b;
176                                                 buffer[count++] = 0x5b;
177                                                 buffer[count++] = cursor[ir[i].Event.KeyEvent.wVirtualKeyCode - 0x25];
178                                         }
179                                 }
180                         }
181                 }
182         }
183         return count;
184 }
185
186 bool OSD::is_console_key_pressed(int vk)
187 {
188         if(use_telnet) {
189                 char temp[256];
190                 int len = 0;
191                 if(cli_socket != INVALID_SOCKET && (len = recv(cli_socket, temp, sizeof(temp), 0)) > 0) {
192                         for(int i = 0; i < len; i++) {
193                                 if(temp[i] == vk) {
194                                         return true;
195                                 }
196                         }
197                 }
198                 if(len == 0) {
199                         telnet_closed = true;
200                         return true;
201                 }
202                 return false;
203         }
204         HWND hWnd = GetForegroundWindow();
205         if(hWnd != NULL && hWnd == FindWindow(_T("ConsoleWindowClass"), NULL)) {
206                 return ((GetAsyncKeyState(vk) & 0x8000) != 0);
207         }
208         return false;
209 }
210
211 bool OSD::is_console_closed()
212 {
213         if(use_telnet) {
214                 return telnet_closed;
215         }
216         return false;
217 }
218
219 void OSD::close_debugger_console()
220 {
221         PostMessage(main_window_handle, WM_COMMAND, ID_CLOSE_DEBUGGER, 0L);
222 }
223
224 const _TCHAR *get_ttermpro_path()
225 {
226         static _TCHAR path[MAX_PATH] = {0};
227         
228         if(_tgetenv(_T("ProgramFiles"))) {
229                 my_stprintf_s(path, MAX_PATH, _T("%s\\teraterm\\ttermpro.exe"), _tgetenv(_T("ProgramFiles")));
230         }
231         return(path);
232 }
233
234 const _TCHAR *get_ttermpro_x86_path()
235 {
236         static _TCHAR path[MAX_PATH] = {0};
237         
238         if(_tgetenv(_T("ProgramFiles(x86)"))) {
239                 my_stprintf_s(path, MAX_PATH, _T("%s\\teraterm\\ttermpro.exe"), _tgetenv(_T("ProgramFiles(x86)")));
240         }
241         return(path);
242 }
243
244 const _TCHAR *get_putty_path()
245 {
246         static _TCHAR path[MAX_PATH] = {0};
247         
248         if(_tgetenv(_T("ProgramFiles"))) {
249                 my_stprintf_s(path, MAX_PATH, _T("%s\\PuTTY\\putty.exe"), _tgetenv(_T("ProgramFiles")));
250         }
251         return(path);
252 }
253
254 const _TCHAR *get_putty_x86_path()
255 {
256         static _TCHAR path[MAX_PATH] = {0};
257         
258         if(_tgetenv(_T("ProgramFiles(x86)"))) {
259                 my_stprintf_s(path, MAX_PATH, _T("%s\\PuTTY\\putty.exe"), _tgetenv(_T("ProgramFiles(x86)")));
260         }
261         return(path);
262 }
263
264 const _TCHAR *get_telnet_path()
265 {
266         static _TCHAR path[MAX_PATH] = {0};
267         
268         if(_tgetenv(_T("windir")) != NULL) {
269 #ifdef _WIN64
270                 my_stprintf_s(path, MAX_PATH, _T("%s\\System32\\telnet.exe"), _tgetenv(_T("windir")));
271 #else
272                 // prevent System32 is redirected to SysWOW64 in 32bit process on Windows x64
273                 my_stprintf_s(path, MAX_PATH, _T("%s\\Sysnative\\telnet.exe"), _tgetenv(_T("windir")));
274 #endif
275         }
276         return(path);
277 }
278
279 const _TCHAR *get_telnet_x86_path()
280 {
281         static _TCHAR path[MAX_PATH] = {0};
282         
283         if(_tgetenv(_T("windir")) != NULL) {
284 #ifdef _WIN64
285                 my_stprintf_s(path, MAX_PATH, _T("%s\\SysWOW64\\telnet.exe"), _tgetenv(_T("windir")));
286 #else
287                 // System32 will be redirected to SysWOW64 in 32bit process on Windows x64
288                 my_stprintf_s(path, MAX_PATH, _T("%s\\System32\\telnet.exe"), _tgetenv(_T("windir")));
289 #endif
290         }
291         return(path);
292 }
293
294 void OSD::open_telnet(const _TCHAR* title)
295 {
296         WSADATA was_data;
297         struct sockaddr_in svr_addr;
298         struct sockaddr_in cli_addr;
299         int cli_addr_len = sizeof(cli_addr);
300         int port = 23;
301         int bind_stat = SOCKET_ERROR;
302         struct timeval timeout;
303         
304         WSAStartup(MAKEWORD(2,0), &was_data);
305         
306         if((svr_socket = socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET) {
307                 memset(&svr_addr, 0, sizeof(svr_addr));
308                 svr_addr.sin_family = AF_INET;
309                 svr_addr.sin_addr.s_addr = htonl(INADDR_ANY);
310                 
311                 while(port < 65536) {
312                         svr_addr.sin_port = htons(port);
313                         if((bind_stat = bind(svr_socket, (struct sockaddr *)&svr_addr, sizeof(svr_addr))) == 0) {
314                                 break;
315                         } else {
316                                 port = (port == 23) ? 49152 : (port + 1);
317                         }
318                 }
319                 if(bind_stat == 0) {
320                         timeout.tv_sec = 1;
321                         timeout.tv_usec = 0;
322                         setsockopt(svr_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
323                         
324                         listen(svr_socket, 1);
325                         
326                         _TCHAR command[MAX_PATH] = {0};
327                         STARTUPINFO si;
328                         PROCESS_INFORMATION pi;
329                         
330                         if(_taccess(get_ttermpro_path(), 0) == 0) {
331                                 my_stprintf_s(command, MAX_PATH, _T("%s localhost:%d /T=1"), get_ttermpro_path(), port);
332                         } else if(_taccess(get_ttermpro_x86_path(), 0) == 0) {
333                                 my_stprintf_s(command, MAX_PATH, _T("%s localhost:%d /T=1"), get_ttermpro_x86_path(), port);
334                         } else if(_taccess(get_putty_path(), 0) == 0) {
335                                 my_stprintf_s(command, MAX_PATH, _T("%s -telnet localhost %d"), get_putty_path(), port);
336                         } else if(_taccess(get_putty_x86_path(), 0) == 0) {
337                                 my_stprintf_s(command, MAX_PATH, _T("%s -telnet localhost %d"), get_putty_x86_path(), port);
338                         } else if(_taccess(get_telnet_path(), 0) == 0) {
339                                 my_stprintf_s(command, MAX_PATH, _T("%s -t vt100 localhost %d"), get_telnet_path(), port);
340                         } else if(_taccess(get_telnet_x86_path(), 0) == 0) {
341                                 my_stprintf_s(command, MAX_PATH, _T("%s -t vt100 localhost %d"), get_telnet_x86_path(), port);
342                         }
343                         if(command[0] != _T('\0')) {
344                                 memset(&si, 0, sizeof(STARTUPINFO));
345                                 memset(&pi, 0, sizeof(PROCESS_INFORMATION));
346                                 CreateProcess(NULL, command, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
347                         }
348                         if((cli_socket = accept(svr_socket, (struct sockaddr *) &cli_addr, &cli_addr_len)) != INVALID_SOCKET) {
349                                 u_long val = 1;
350                                 ioctlsocket(cli_socket, FIONBIO, &val);
351                                 telnet_closed = false;
352 #if 0
353                                 sprintf_s(command, MAX_PATH, "\033]0;%s\007", tchar_to_char(title));
354                                 send_telnet(command);
355 #endif
356                                 uint8_t will_echo[] = {0xff, 0xfb, 0x01};
357                                 send(cli_socket, (char *)will_echo, 3, 0);
358                                 send_telnet("\033[2l");  // key unlock
359                                 send_telnet("\033[12h"); // local echo off
360                         }
361                 }
362         }
363 }
364
365 void OSD::close_telnet()
366 {
367         if(svr_socket != INVALID_SOCKET) {
368                 shutdown(svr_socket, /*SD_BOTH*/2);
369                 closesocket(svr_socket);
370                 svr_socket = cli_socket = INVALID_SOCKET;
371         }
372         WSACleanup();
373 }
374
375 void OSD::send_telnet(const char* string)
376 {
377         if(cli_socket != INVALID_SOCKET) {
378                 for(unsigned int i = 0; i < strlen(string); i++) {
379                         if(string[i] == 0x0d || string[i] == 0x0a) {
380                                 send_telnet("\033E");
381                         } else if(string[i] == 0x08) {
382                                 send_telnet("\033[1D");
383                         } else {
384                                 send(cli_socket, &string[i], 1, 0);
385                         }
386                 }
387         }
388 }