1 // Copyright (C) 2009-2012 Gaz Davidson
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
5 #include "CIrrDeviceConsole.h"
\r
7 #ifdef _IRR_COMPILE_WITH_CONSOLE_DEVICE_
\r
10 #include "IGUISkin.h"
\r
11 #include "IGUIEnvironment.h"
\r
13 // to close the device on terminate signal
\r
14 irr::CIrrDeviceConsole *DeviceToClose;
\r
16 #ifdef _IRR_WINDOWS_NT_CONSOLE_
\r
17 // Callback for Windows
\r
18 BOOL WINAPI ConsoleHandler(DWORD CEvent)
\r
23 irr::os::Printer::log("Closing console device", "CTRL+C");
\r
25 case CTRL_BREAK_EVENT:
\r
26 irr::os::Printer::log("Closing console device", "CTRL+Break");
\r
28 case CTRL_CLOSE_EVENT:
\r
29 irr::os::Printer::log("Closing console device", "User closed console");
\r
31 case CTRL_LOGOFF_EVENT:
\r
32 irr::os::Printer::log("Closing console device", "User is logging off");
\r
34 case CTRL_SHUTDOWN_EVENT:
\r
35 irr::os::Printer::log("Closing console device", "Computer shutting down");
\r
38 DeviceToClose->closeDevice();
\r
41 #elif defined(_IRR_POSIX_API_)
\r
45 void sighandler(int sig)
\r
47 irr::core::stringc code = "Signal ";
\r
49 code += " received";
\r
50 irr::os::Printer::log("Closing console device", code.c_str());
\r
52 DeviceToClose->closeDevice();
\r
59 const c8 ASCIIArtChars[] = " .,'~:;!+>=icopjtJY56SB8XDQKHNWM"; //MWNHKQDX8BS65YJtjpoci=+>!;:~',. ";
\r
60 const u16 ASCIIArtCharsCount = 32;
\r
62 //const c8 ASCIIArtChars[] = " \xb0\xb1\xf9\xb2\xdb";
\r
63 //const u16 ASCIIArtCharsCount = 5;
\r
66 CIrrDeviceConsole::CIrrDeviceConsole(const SIrrlichtCreationParameters& params)
\r
67 : CIrrDeviceStub(params), IsWindowFocused(true), ConsoleFont(0), OutFile(stdout)
\r
69 DeviceToClose = this;
\r
71 #ifdef _IRR_WINDOWS_NT_CONSOLE_
\r
72 MouseButtonStates = 0;
\r
74 WindowsSTDIn = GetStdHandle(STD_INPUT_HANDLE);
\r
75 WindowsSTDOut = GetStdHandle(STD_OUTPUT_HANDLE);
\r
77 if (CreationParams.Fullscreen)
\r
79 PCOORD dimensions = 0;
\r
80 if (SetConsoleDisplayMode(WindowsSTDOut, CONSOLE_FULLSCREEN_MODE, dimensions))
\r
82 CreationParams.WindowSize.Width = dimensions->X;
\r
83 CreationParams.WindowSize.Width = dimensions->Y;
\r
89 ConsoleSize.X = CreationParams.WindowSize.Width;
\r
90 ConsoleSize.X = CreationParams.WindowSize.Height;
\r
91 SetConsoleScreenBufferSize(WindowsSTDOut, ConsoleSize);
\r
94 // catch windows close/break signals
\r
95 SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler, TRUE);
\r
97 #elif defined(_IRR_POSIX_API_)
\r
98 // catch other signals
\r
99 signal(SIGABRT, &sighandler);
\r
100 signal(SIGTERM, &sighandler);
\r
101 signal(SIGINT, &sighandler);
\r
103 // set output stream
\r
104 if (params.WindowId)
\r
105 OutFile = (FILE*)(params.WindowId);
\r
108 #ifdef _IRR_VT100_CONSOLE_
\r
110 fprintf(OutFile, "%cc", 27);
\r
111 // disable line wrapping
\r
112 fprintf(OutFile, "%c[7l", 27);
\r
115 switch (params.DriverType)
\r
117 case video::EDT_SOFTWARE:
\r
118 #ifdef _IRR_COMPILE_WITH_SOFTWARE_
\r
119 VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this);
\r
121 os::Printer::log("Software driver was not compiled in.", ELL_ERROR);
\r
125 case video::EDT_BURNINGSVIDEO:
\r
126 #ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
\r
127 VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this);
\r
129 os::Printer::log("Burning's Video driver was not compiled in.", ELL_ERROR);
\r
133 case video::EDT_DIRECT3D9:
\r
134 case video::EDT_OPENGL:
\r
135 os::Printer::log("The console device cannot use hardware drivers yet.", ELL_ERROR);
\r
137 case video::EDT_NULL:
\r
138 VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
\r
141 os::Printer::log("Unsupported device.", ELL_ERROR);
\r
145 // set up output buffer
\r
146 for (u32 y=0; y<CreationParams.WindowSize.Height; ++y)
\r
149 str.reserve(CreationParams.WindowSize.Width);
\r
150 for (u32 x=0; x<CreationParams.WindowSize.Width; ++x)
\r
152 OutputBuffer.push_back(str);
\r
156 #ifdef _IRR_WINDOWS_NT_CONSOLE_
\r
157 CursorControl = new CCursorControl(CreationParams.WindowSize);
\r
162 createGUIAndScene();
\r
163 #ifdef _IRR_USE_CONSOLE_FONT_
\r
164 if (GUIEnvironment)
\r
166 ConsoleFont = new gui::CGUIConsoleFont(this);
\r
167 gui::IGUISkin *skin = GUIEnvironment->getSkin();
\r
170 for (u32 i=0; i < gui::EGDF_COUNT; ++i)
\r
171 skin->setFont(ConsoleFont, gui::EGUI_DEFAULT_FONT(i));
\r
179 CIrrDeviceConsole::~CIrrDeviceConsole()
\r
181 // GUI and scene are dropped in the stub
\r
184 CursorControl->drop();
\r
189 ConsoleFont->drop();
\r
192 #ifdef _IRR_VT100_CONSOLE_
\r
194 fprintf(OutFile, "%cc", 27);
\r
198 //! runs the device. Returns false if device wants to be deleted
\r
199 bool CIrrDeviceConsole::run()
\r
204 // process Windows console input
\r
205 #ifdef _IRR_WINDOWS_NT_CONSOLE_
\r
209 DWORD count, waste;
\r
211 // get old input mode
\r
212 GetConsoleMode(WindowsSTDIn, &oldMode);
\r
213 SetConsoleMode(WindowsSTDIn, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
\r
215 GetNumberOfConsoleInputEvents(WindowsSTDIn, &count);
\r
217 // read keyboard and mouse input
\r
220 ReadConsoleInput(WindowsSTDIn, &in, 1, &waste );
\r
221 switch(in.EventType)
\r
226 e.EventType = EET_KEY_INPUT_EVENT;
\r
227 e.KeyInput.PressedDown = (in.Event.KeyEvent.bKeyDown == TRUE);
\r
228 e.KeyInput.Control = (in.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0;
\r
229 e.KeyInput.Shift = (in.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) != 0;
\r
230 e.KeyInput.Key = EKEY_CODE(in.Event.KeyEvent.wVirtualKeyCode);
\r
231 e.KeyInput.Char = in.Event.KeyEvent.uChar.UnicodeChar;
\r
232 postEventFromUser(e);
\r
238 e.EventType = EET_MOUSE_INPUT_EVENT;
\r
239 e.MouseInput.X = in.Event.MouseEvent.dwMousePosition.X;
\r
240 e.MouseInput.Y = in.Event.MouseEvent.dwMousePosition.Y;
\r
241 e.MouseInput.Wheel = 0.f;
\r
242 e.MouseInput.ButtonStates =
\r
243 ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) ? EMBSM_LEFT : 0 ) |
\r
244 ( (in.Event.MouseEvent.dwButtonState & RIGHTMOST_BUTTON_PRESSED) ? EMBSM_RIGHT : 0 ) |
\r
245 ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) ? EMBSM_MIDDLE : 0 ) |
\r
246 ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_3RD_BUTTON_PRESSED) ? EMBSM_EXTRA1 : 0 ) |
\r
247 ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_4TH_BUTTON_PRESSED) ? EMBSM_EXTRA2 : 0 );
\r
249 if (in.Event.MouseEvent.dwEventFlags & MOUSE_MOVED)
\r
251 CursorControl->setPosition(core::position2di(e.MouseInput.X, e.MouseInput.Y));
\r
253 // create mouse moved event
\r
254 e.MouseInput.Event = EMIE_MOUSE_MOVED;
\r
255 postEventFromUser(e);
\r
258 if (in.Event.MouseEvent.dwEventFlags & MOUSE_WHEELED)
\r
260 e.MouseInput.Event = EMIE_MOUSE_WHEEL;
\r
261 e.MouseInput.Wheel = (in.Event.MouseEvent.dwButtonState & 0xFF000000) ? -1.0f : 1.0f;
\r
262 postEventFromUser(e);
\r
265 if ( (MouseButtonStates & EMBSM_LEFT) != (e.MouseInput.ButtonStates & EMBSM_LEFT) )
\r
267 e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_LEFT) ? EMIE_LMOUSE_PRESSED_DOWN : EMIE_LMOUSE_LEFT_UP;
\r
268 postEventFromUser(e);
\r
271 if ( (MouseButtonStates & EMBSM_RIGHT) != (e.MouseInput.ButtonStates & EMBSM_RIGHT) )
\r
273 e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_RIGHT) ? EMIE_RMOUSE_PRESSED_DOWN : EMIE_RMOUSE_LEFT_UP;
\r
274 postEventFromUser(e);
\r
277 if ( (MouseButtonStates & EMBSM_MIDDLE) != (e.MouseInput.ButtonStates & EMBSM_MIDDLE) )
\r
279 e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_MIDDLE) ? EMIE_MMOUSE_PRESSED_DOWN : EMIE_MMOUSE_LEFT_UP;
\r
280 postEventFromUser(e);
\r
283 // save current button states
\r
284 MouseButtonStates = e.MouseInput.ButtonStates;
\r
288 case WINDOW_BUFFER_SIZE_EVENT:
\r
289 VideoDriver->OnResize(
\r
290 core::dimension2d<u32>(in.Event.WindowBufferSizeEvent.dwSize.X,
\r
291 in.Event.WindowBufferSizeEvent.dwSize.Y));
\r
294 IsWindowFocused = (in.Event.FocusEvent.bSetFocus == TRUE);
\r
299 GetNumberOfConsoleInputEvents(WindowsSTDIn, &count);
\r
303 SetConsoleMode(WindowsSTDIn, oldMode);
\r
305 // todo: keyboard input from terminal in raw mode
\r
311 //! Cause the device to temporarily pause execution and let other processes to run
\r
312 // This should bring down processor usage without major performance loss for Irrlicht
\r
313 void CIrrDeviceConsole::yield()
\r
315 #ifdef _IRR_WINDOWS_API_
\r
318 struct timespec ts = {0,0};
\r
319 nanosleep(&ts, NULL);
\r
323 //! Pause execution and let other processes to run for a specified amount of time.
\r
324 void CIrrDeviceConsole::sleep(u32 timeMs, bool pauseTimer)
\r
326 const bool wasStopped = Timer ? Timer->isStopped() : true;
\r
328 #ifdef _IRR_WINDOWS_API_
\r
331 struct timespec ts;
\r
332 ts.tv_sec = (time_t) (timeMs / 1000);
\r
333 ts.tv_nsec = (long) (timeMs % 1000) * 1000000;
\r
335 if (pauseTimer && !wasStopped)
\r
338 nanosleep(&ts, NULL);
\r
341 if (pauseTimer && !wasStopped)
\r
345 //! sets the caption of the window
\r
346 void CIrrDeviceConsole::setWindowCaption(const wchar_t* text)
\r
348 #ifdef _IRR_WINDOWS_NT_CONSOLE_
\r
349 SetConsoleTitleW(text);
\r
353 //! returns if window is active. if not, nothing need to be drawn
\r
354 bool CIrrDeviceConsole::isWindowActive() const
\r
356 // there is no window, but we always assume it is active
\r
360 //! returns if window has focus
\r
361 bool CIrrDeviceConsole::isWindowFocused() const
\r
363 return IsWindowFocused;
\r
366 //! returns if window is minimized
\r
367 bool CIrrDeviceConsole::isWindowMinimized() const
\r
372 //! presents a surface in the client area
\r
373 bool CIrrDeviceConsole::present(video::IImage* surface, void* windowId, core::rect<s32>* src)
\r
378 for (u32 y=0; y < surface->getDimension().Height; ++y)
\r
380 for (u32 x=0; x< surface->getDimension().Width; ++x)
\r
382 // get average pixel
\r
383 u32 avg = surface->getPixel(x,y).getAverage() * (ASCIIArtCharsCount-1);
\r
385 OutputBuffer[y] [x] = ASCIIArtChars[avg];
\r
389 #ifdef _IRR_USE_CONSOLE_FONT_
\r
390 for (u32 i=0; i< Text.size(); ++i)
\r
392 s32 y = Text[i].Pos.Y;
\r
394 if ( y < (s32)OutputBuffer.size() && y > 0)
\r
395 for (u32 c=0; c < Text[i].Text.size() && c + Text[i].Pos.X < OutputBuffer[y].size(); ++c)
\r
396 //if (Text[i].Text[c] != ' ')
\r
397 OutputBuffer[y] [c+Text[i].Pos.X] = Text[i].Text[c];
\r
403 for (u32 y=0; y<OutputBuffer.size(); ++y)
\r
405 setTextCursorPos(0,y);
\r
406 fprintf(OutFile, "%s", OutputBuffer[y].c_str());
\r
408 return surface != 0;
\r
411 //! notifies the device that it should close itself
\r
412 void CIrrDeviceConsole::closeDevice()
\r
414 // return false next time we run()
\r
419 //! Sets if the window should be resizable in windowed mode.
\r
420 void CIrrDeviceConsole::setResizable(bool resize)
\r
426 //! Minimize the window.
\r
427 void CIrrDeviceConsole::minimizeWindow()
\r
433 //! Maximize window
\r
434 void CIrrDeviceConsole::maximizeWindow()
\r
440 //! Restore original window size
\r
441 void CIrrDeviceConsole::restoreWindow()
\r
447 void CIrrDeviceConsole::setTextCursorPos(s16 x, s16 y)
\r
449 #ifdef _IRR_WINDOWS_NT_CONSOLE_
\r
450 // move WinNT cursor
\r
454 SetConsoleCursorPosition(WindowsSTDOut, Position);
\r
455 #elif defined(_IRR_VT100_CONSOLE_)
\r
456 // send escape code
\r
457 fprintf(OutFile, "%c[%d;%dH", 27, y, x);
\r
463 void CIrrDeviceConsole::addPostPresentText(s16 X, s16 Y, const wchar_t *text)
\r
465 SPostPresentText p;
\r
472 } // end namespace irr
\r
474 #endif // _IRR_COMPILE_WITH_CONSOLE_DEVICE_
\r