1 // Copyright (C) 2009-2012 Gaz Davidson
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "CIrrDeviceConsole.h"
6 
7 #ifdef _IRR_COMPILE_WITH_CONSOLE_DEVICE_
8 
9 #include "os.h"
10 #include "IGUISkin.h"
11 #include "IGUIEnvironment.h"
12 
13 // to close the device on terminate signal
14 irr::CIrrDeviceConsole *DeviceToClose;
15 
16 #ifdef _IRR_WINDOWS_NT_CONSOLE_
17 // Callback for Windows
ConsoleHandler(DWORD CEvent)18 BOOL WINAPI ConsoleHandler(DWORD CEvent)
19 {
20     switch(CEvent)
21     {
22     case CTRL_C_EVENT:
23 		irr::os::Printer::log("Closing console device", "CTRL+C");
24 		break;
25 	case CTRL_BREAK_EVENT:
26 		irr::os::Printer::log("Closing console device", "CTRL+Break");
27 		break;
28     case CTRL_CLOSE_EVENT:
29 		irr::os::Printer::log("Closing console device", "User closed console");
30 		break;
31     case CTRL_LOGOFF_EVENT:
32 		irr::os::Printer::log("Closing console device", "User is logging off");
33 		break;
34     case CTRL_SHUTDOWN_EVENT:
35 		irr::os::Printer::log("Closing console device", "Computer shutting down");
36 		break;
37     }
38 	DeviceToClose->closeDevice();
39     return TRUE;
40 }
41 #elif defined(_IRR_POSIX_API_)
42 // sigterm handler
43 #include <signal.h>
44 
sighandler(int sig)45 void sighandler(int sig)
46 {
47 	irr::core::stringc code = "Signal ";
48 	code += sig;
49 	code += " received";
50 	irr::os::Printer::log("Closing console device", code.c_str());
51 
52 	DeviceToClose->closeDevice();
53 }
54 #endif
55 
56 namespace irr
57 {
58 
59 const c8 ASCIIArtChars[] = " .,'~:;!+>=icopjtJY56SB8XDQKHNWM"; //MWNHKQDX8BS65YJtjpoci=+>!;:~',. ";
60 const u16 ASCIIArtCharsCount = 32;
61 
62 //const c8 ASCIIArtChars[] = " \xb0\xb1\xf9\xb2\xdb";
63 //const u16 ASCIIArtCharsCount = 5;
64 
65 //! constructor
CIrrDeviceConsole(const SIrrlichtCreationParameters & params)66 CIrrDeviceConsole::CIrrDeviceConsole(const SIrrlichtCreationParameters& params)
67   : CIrrDeviceStub(params), IsWindowFocused(true), ConsoleFont(0), OutFile(stdout)
68 {
69 	DeviceToClose = this;
70 
71 #ifdef _IRR_WINDOWS_NT_CONSOLE_
72 	MouseButtonStates = 0;
73 
74 	WindowsSTDIn  = GetStdHandle(STD_INPUT_HANDLE);
75 	WindowsSTDOut = GetStdHandle(STD_OUTPUT_HANDLE);
76 	PCOORD Dimensions = 0;
77 
78 	if (CreationParams.Fullscreen)
79 	{
80 // Some mingw versions lack this define, so avoid it in case it does not exist
81 #if (_WIN32_WINNT >= 0x0501) && defined(CONSOLE_FULLSCREEN_MODE)
82 		if (SetConsoleDisplayMode(WindowsSTDOut, CONSOLE_FULLSCREEN_MODE, Dimensions))
83 		{
84 			CreationParams.WindowSize.Width = Dimensions->X;
85 			CreationParams.WindowSize.Width = Dimensions->Y;
86 		}
87 #endif
88 	}
89 	else
90 	{
91 		COORD ConsoleSize;
92 		ConsoleSize.X = CreationParams.WindowSize.Width;
93 		ConsoleSize.X = CreationParams.WindowSize.Height;
94 		SetConsoleScreenBufferSize(WindowsSTDOut, ConsoleSize);
95 	}
96 
97 	// catch windows close/break signals
98 	SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler, TRUE);
99 
100 #elif defined(_IRR_POSIX_API_)
101 	// catch other signals
102 	signal(SIGABRT, &sighandler);
103 	signal(SIGTERM, &sighandler);
104 	signal(SIGINT,  &sighandler);
105 
106 	// set output stream
107 	if (params.WindowId)
108 		OutFile = (FILE*)(params.WindowId);
109 #endif
110 
111 #ifdef _IRR_VT100_CONSOLE_
112 	// reset terminal
113 	fprintf(OutFile, "%cc", 27);
114 	// disable line wrapping
115 	fprintf(OutFile, "%c[7l", 27);
116 #endif
117 
118 	switch (params.DriverType)
119 	{
120 	case video::EDT_SOFTWARE:
121 		#ifdef _IRR_COMPILE_WITH_SOFTWARE_
122 		VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this);
123 		#else
124 		os::Printer::log("Software driver was not compiled in.", ELL_ERROR);
125 		#endif
126 		break;
127 
128 	case video::EDT_BURNINGSVIDEO:
129 		#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
130 		VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this);
131 		#else
132 		os::Printer::log("Burning's Video driver was not compiled in.", ELL_ERROR);
133 		#endif
134 		break;
135 
136 	case video::EDT_DIRECT3D8:
137 	case video::EDT_DIRECT3D9:
138 	case video::EDT_OPENGL:
139 		os::Printer::log("The console device cannot use hardware drivers yet.", ELL_ERROR);
140 		break;
141 	case video::EDT_NULL:
142 		VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
143 		break;
144 	default:
145 		break;
146 	}
147 
148 	// set up output buffer
149 	for (u32 y=0; y<CreationParams.WindowSize.Height; ++y)
150 	{
151 		core::stringc str;
152 		str.reserve(CreationParams.WindowSize.Width);
153 		for (u32 x=0; x<CreationParams.WindowSize.Width; ++x)
154 			str += " ";
155 		OutputBuffer.push_back(str);
156 	}
157 
158 
159 #ifdef _IRR_WINDOWS_NT_CONSOLE_
160 	CursorControl = new CCursorControl(CreationParams.WindowSize);
161 #endif
162 
163 	if (VideoDriver)
164 	{
165 		createGUIAndScene();
166 #ifdef _IRR_USE_CONSOLE_FONT_
167 		if (GUIEnvironment)
168 		{
169 			ConsoleFont = new gui::CGUIConsoleFont(this);
170 			gui::IGUISkin *skin = GUIEnvironment->getSkin();
171 			if (skin)
172 			{
173 				for (u32 i=0; i < gui::EGDF_COUNT; ++i)
174 					skin->setFont(ConsoleFont, gui::EGUI_DEFAULT_FONT(i));
175 			}
176 		}
177 #endif
178 	}
179 }
180 
181 //! destructor
~CIrrDeviceConsole()182 CIrrDeviceConsole::~CIrrDeviceConsole()
183 {
184 	// GUI and scene are dropped in the stub
185 	if (CursorControl)
186 	{
187 		CursorControl->drop();
188 		CursorControl = 0;
189 	}
190 	if (ConsoleFont)
191 	{
192 		ConsoleFont->drop();
193 		ConsoleFont = 0;
194 	}
195 #ifdef _IRR_VT100_CONSOLE_
196 	// reset terminal
197 	fprintf(OutFile, "%cc", 27);
198 #endif
199 }
200 
201 //! runs the device. Returns false if device wants to be deleted
run()202 bool CIrrDeviceConsole::run()
203 {
204 	// increment timer
205 	os::Timer::tick();
206 
207 	// process Windows console input
208 #ifdef _IRR_WINDOWS_NT_CONSOLE_
209 
210 	INPUT_RECORD in;
211 	DWORD        oldMode;
212 	DWORD        count, waste;
213 
214 	// get old input mode
215 	GetConsoleMode(WindowsSTDIn, &oldMode);
216 	SetConsoleMode(WindowsSTDIn, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
217 
218 	GetNumberOfConsoleInputEvents(WindowsSTDIn, &count);
219 
220 	// read keyboard and mouse input
221 	while (count)
222 	{
223 		ReadConsoleInput(WindowsSTDIn, &in, 1, &waste );
224 		switch(in.EventType)
225 		{
226 		case KEY_EVENT:
227 		{
228 			SEvent e;
229 			e.EventType            = EET_KEY_INPUT_EVENT;
230 			e.KeyInput.PressedDown = (in.Event.KeyEvent.bKeyDown == TRUE);
231 			e.KeyInput.Control     = (in.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0;
232 			e.KeyInput.Shift       = (in.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) != 0;
233 			e.KeyInput.Key         = EKEY_CODE(in.Event.KeyEvent.wVirtualKeyCode);
234 			e.KeyInput.Char        = in.Event.KeyEvent.uChar.UnicodeChar;
235 			postEventFromUser(e);
236 			break;
237 		}
238 		case MOUSE_EVENT:
239 		{
240 			SEvent e;
241 			e.EventType        = EET_MOUSE_INPUT_EVENT;
242 			e.MouseInput.X     = in.Event.MouseEvent.dwMousePosition.X;
243 			e.MouseInput.Y     = in.Event.MouseEvent.dwMousePosition.Y;
244 			e.MouseInput.Wheel = 0.f;
245 			e.MouseInput.ButtonStates =
246 				( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) ? EMBSM_LEFT   : 0 ) |
247 				( (in.Event.MouseEvent.dwButtonState & RIGHTMOST_BUTTON_PRESSED)     ? EMBSM_RIGHT  : 0 ) |
248 				( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) ? EMBSM_MIDDLE : 0 ) |
249 				( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_3RD_BUTTON_PRESSED) ? EMBSM_EXTRA1 : 0 ) |
250 				( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_4TH_BUTTON_PRESSED) ? EMBSM_EXTRA2 : 0 );
251 
252 			if (in.Event.MouseEvent.dwEventFlags & MOUSE_MOVED)
253 			{
254 				CursorControl->setPosition(core::position2di(e.MouseInput.X, e.MouseInput.Y));
255 
256 				// create mouse moved event
257 				e.MouseInput.Event = EMIE_MOUSE_MOVED;
258 				postEventFromUser(e);
259 			}
260 
261 			if (in.Event.MouseEvent.dwEventFlags & MOUSE_WHEELED)
262 			{
263 				e.MouseInput.Event = EMIE_MOUSE_WHEEL;
264 				e.MouseInput.Wheel = (in.Event.MouseEvent.dwButtonState & 0xFF000000) ? -1.0f : 1.0f;
265 				postEventFromUser(e);
266 			}
267 
268 			if ( (MouseButtonStates & EMBSM_LEFT) != (e.MouseInput.ButtonStates & EMBSM_LEFT) )
269 			{
270 				e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_LEFT) ? EMIE_LMOUSE_PRESSED_DOWN : EMIE_LMOUSE_LEFT_UP;
271 				postEventFromUser(e);
272 			}
273 
274 			if ( (MouseButtonStates & EMBSM_RIGHT) != (e.MouseInput.ButtonStates & EMBSM_RIGHT) )
275 			{
276 				e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_RIGHT) ? EMIE_RMOUSE_PRESSED_DOWN : EMIE_RMOUSE_LEFT_UP;
277 				postEventFromUser(e);
278 			}
279 
280 			if ( (MouseButtonStates & EMBSM_MIDDLE) != (e.MouseInput.ButtonStates & EMBSM_MIDDLE) )
281 			{
282 				e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_MIDDLE) ? EMIE_MMOUSE_PRESSED_DOWN : EMIE_MMOUSE_LEFT_UP;
283 				postEventFromUser(e);
284 			}
285 
286 			// save current button states
287 			MouseButtonStates = e.MouseInput.ButtonStates;
288 
289 			break;
290 		}
291 		case WINDOW_BUFFER_SIZE_EVENT:
292 			VideoDriver->OnResize(
293 				core::dimension2d<u32>(in.Event.WindowBufferSizeEvent.dwSize.X,
294 				                       in.Event.WindowBufferSizeEvent.dwSize.Y));
295 			break;
296 		case FOCUS_EVENT:
297 			IsWindowFocused = (in.Event.FocusEvent.bSetFocus == TRUE);
298 			break;
299 		default:
300 			break;
301 		}
302 		GetNumberOfConsoleInputEvents(WindowsSTDIn, &count);
303 	}
304 
305 	// set input mode
306 	SetConsoleMode(WindowsSTDIn, oldMode);
307 #else
308 	// todo: keyboard input from terminal in raw mode
309 #endif
310 
311 	return !Close;
312 }
313 
314 //! Cause the device to temporarily pause execution and let other processes to run
315 // This should bring down processor usage without major performance loss for Irrlicht
yield()316 void CIrrDeviceConsole::yield()
317 {
318 #ifdef _IRR_WINDOWS_API_
319 	Sleep(1);
320 #else
321 	struct timespec ts = {0,0};
322 	nanosleep(&ts, NULL);
323 #endif
324 }
325 
326 //! Pause execution and let other processes to run for a specified amount of time.
sleep(u32 timeMs,bool pauseTimer)327 void CIrrDeviceConsole::sleep(u32 timeMs, bool pauseTimer)
328 {
329 	const bool wasStopped = Timer ? Timer->isStopped() : true;
330 
331 #ifdef _IRR_WINDOWS_API_
332 	Sleep(timeMs);
333 #else
334 	struct timespec ts;
335 	ts.tv_sec = (time_t) (timeMs / 1000);
336 	ts.tv_nsec = (long) (timeMs % 1000) * 1000000;
337 
338 	if (pauseTimer && !wasStopped)
339 		Timer->stop();
340 
341 	nanosleep(&ts, NULL);
342 #endif
343 
344 	if (pauseTimer && !wasStopped)
345 		Timer->start();
346 }
347 
348 //! sets the caption of the window
setWindowCaption(const wchar_t * text)349 void CIrrDeviceConsole::setWindowCaption(const wchar_t* text)
350 {
351 #ifdef _IRR_WINDOWS_NT_CONSOLE_
352 	SetConsoleTitleW(text);
353 #endif
354 }
355 
356 //! returns if window is active. if not, nothing need to be drawn
isWindowActive() const357 bool CIrrDeviceConsole::isWindowActive() const
358 {
359 	// there is no window, but we always assume it is active
360 	return true;
361 }
362 
363 //! returns if window has focus
isWindowFocused() const364 bool CIrrDeviceConsole::isWindowFocused() const
365 {
366 	return IsWindowFocused;
367 }
368 
369 //! returns if window is minimized
isWindowMinimized() const370 bool CIrrDeviceConsole::isWindowMinimized() const
371 {
372 	return false;
373 }
374 
375 //! presents a surface in the client area
present(video::IImage * surface,void * windowId,core::rect<s32> * src)376 bool CIrrDeviceConsole::present(video::IImage* surface, void* windowId, core::rect<s32>* src)
377 {
378 
379 	if (surface)
380 	{
381 		for (u32 y=0; y < surface->getDimension().Height; ++y)
382 		{
383 			for (u32 x=0; x< surface->getDimension().Width; ++x)
384 			{
385 				// get average pixel
386 				u32 avg = surface->getPixel(x,y).getAverage() * (ASCIIArtCharsCount-1);
387 				avg /= 255;
388 				OutputBuffer[y] [x] = ASCIIArtChars[avg];
389 			}
390 		}
391 	}
392 #ifdef _IRR_USE_CONSOLE_FONT_
393 	for (u32 i=0; i< Text.size(); ++i)
394 	{
395 		s32 y = Text[i].Pos.Y;
396 
397 		if ( y < (s32)OutputBuffer.size() && y > 0)
398 			for (u32 c=0; c < Text[i].Text.size() && c + Text[i].Pos.X < OutputBuffer[y].size(); ++c)
399 				//if (Text[i].Text[c] != ' ')
400 				OutputBuffer[y] [c+Text[i].Pos.X] = Text[i].Text[c];
401 	}
402 	Text.clear();
403 #endif
404 
405 	// draw output
406 	for (u32 y=0; y<OutputBuffer.size(); ++y)
407 	{
408 		setTextCursorPos(0,y);
409 		fprintf(OutFile, "%s", OutputBuffer[y].c_str());
410 	}
411 	return surface != 0;
412 }
413 
414 //! notifies the device that it should close itself
closeDevice()415 void CIrrDeviceConsole::closeDevice()
416 {
417 	// return false next time we run()
418 	Close = true;
419 }
420 
421 
422 //! Sets if the window should be resizable in windowed mode.
setResizable(bool resize)423 void CIrrDeviceConsole::setResizable(bool resize)
424 {
425 	// do nothing
426 }
427 
428 
429 //! Minimize the window.
minimizeWindow()430 void CIrrDeviceConsole::minimizeWindow()
431 {
432 	// do nothing
433 }
434 
435 
436 //! Maximize window
maximizeWindow()437 void CIrrDeviceConsole::maximizeWindow()
438 {
439 	// do nothing
440 }
441 
442 
443 //! Restore original window size
restoreWindow()444 void CIrrDeviceConsole::restoreWindow()
445 {
446 	// do nothing
447 }
448 
449 
setTextCursorPos(s16 x,s16 y)450 void CIrrDeviceConsole::setTextCursorPos(s16 x, s16 y)
451 {
452 #ifdef _IRR_WINDOWS_NT_CONSOLE_
453 	// move WinNT cursor
454 	COORD Position;
455     Position.X = x;
456     Position.Y = y;
457     SetConsoleCursorPosition(WindowsSTDOut, Position);
458 #elif defined(_IRR_VT100_CONSOLE_)
459 	// send escape code
460 	fprintf(OutFile, "%c[%d;%dH", 27, y, x);
461 #else
462 	// not implemented
463 #endif
464 }
465 
addPostPresentText(s16 X,s16 Y,const wchar_t * text)466 void CIrrDeviceConsole::addPostPresentText(s16 X, s16 Y, const wchar_t *text)
467 {
468 	SPostPresentText p;
469 	p.Text = text;
470 	p.Pos.X = X;
471 	p.Pos.Y = Y;
472 	Text.push_back(p);
473 }
474 
475 } // end namespace irr
476 
477 #endif // _IRR_COMPILE_WITH_CONSOLE_DEVICE_
478